Appearance
Campaigns and UTM endpoints
These REST routes manage campaigns and report on the clicks of the short links tagged with a campaign value. They also store reusable UTM templates. Each entry lists the parameters, the response fields and the errors.
Sign-in, the response envelope and paging work as in the REST API overview.
All paths are relative to the base URL /wp-json/link-manager-pro/v1. Examples use https://example.com and an Application Password placeholder (USER:APPLICATION_PASSWORD). Inside WordPress, send the X-WP-Nonce header instead. Every route on this page needs link_manager_pro_access (Permissions).
What a campaign is
A campaign is one utm_campaign value. Two things can make a campaign exist:
- Stored campaign. A row in the
link_manager_pro_metatable withentity_typecampaign,entity_id0and the campaign value asmeta_key. The row keeps a name, a description and default UTM values. The API calls itmanaged: true. - Campaign value found on links. Any short link whose saved UTM parameters contain a
utm_campaignvalue counts as part of that campaign, stored or not. A campaign that exists only this way hasmanaged: false. The list screen calls it a virtual campaign, andtype=virtualselects it.
The list route merges both. A partner's own utm_campaign value also creates a list row, with link_count 0 unless links use it. Deleting a stored campaign removes only its row. The links keep their UTM values, so the campaign shows again as managed: false.
The campaign value is called slug in every response and is the key used in the path. It is not a short link slug.
Campaign values in the path
The routes PUT /campaigns/{campaign}, DELETE /campaigns/{campaign} and every /campaigns/{name}/... route take the value from the path.
- The path pattern matches
[^/]+, so a value that contains/cannot be addressed by these routes. - The plugin runs
rawurldecodeon the value, removes tags and trims it. Send the value URL-encoded. A non-Latin value such asвесна-2026works when encoded. - Lookup of a stored campaign also tries the value as given, decoded once and the older percent-encoded lower-case spelling. A campaign stored by an earlier version under
%d0%b2...still opens. - Links are matched by the exact value in their UTM parameters.
Timing and caching
Campaign figures are cached for 60 seconds, and creating, updating or deleting a campaign clears that cache. A report can lag behind a new click or a link edit by up to a minute.
Error shapes
An unknown campaign is not handled the same way on every route:
| Route | Unknown campaign |
|---|---|
PUT /campaigns/{campaign} | 422, with errors.slug "Campaign not found." (after the name check). |
DELETE /campaigns/{campaign} | 404. |
GET /campaigns/{name}/report and .../report/export | 404. |
GET /campaigns/{name}/links | 200 with an empty page. |
GET /campaigns/{name}/clicks | 200 with zero-filled days. |
GET /campaigns/{name}/utm-breakdown | 200 with []. |
GET /campaigns/{name}/countries | 200 with no rows. |
A 404 from this page is a WordPress error { "code": "campaign_not_found", "message": "Campaign not found", "data": { "status": 404 } }. Validation failures are 422 as in the overview.
The campaign object
Two shapes carry a campaign. The stored campaign comes from GET /campaigns/all, create and update. The list row comes from GET /campaigns.
Stored campaign (all, create and update)
| Field | Type | What it holds |
|---|---|---|
id | integer | The ID of the row in link_manager_pro_meta. |
slug | string | The campaign value, the utm_campaign text. |
name | string | The display name. |
description | string | The description, empty when none. |
utm_source | string | Default utm_source for new links in this campaign. Empty when none. |
utm_medium | string | Default utm_medium. Empty when none. |
utm_term | string | Default utm_term. Empty when none. |
utm_content | string | Default utm_content. Empty when none. |
link_count | integer | Links whose UTM parameters carry this value. Create and update only. |
affiliate_count | integer | Affiliates whose UTM campaign is this value. Create and update only. |
List row
| Field | Type | What it holds |
|---|---|---|
slug | string | The campaign value. |
name | string | The stored name, or the value itself for an unmanaged campaign. |
description | string | The stored description, empty otherwise. |
managed | boolean | true for a stored campaign, false when only links carry the value. |
link_count | integer | Short links carrying the value. |
affiliate_count | integer | Affiliates whose UTM campaign is this value. |
total_clicks | integer | Recorded clicks on those links. |
unique_clicks | integer | Recorded clicks marked unique on those links. |
sources | array of strings | The distinct utm_source values of those links, and of a partner using the value. |
mediums | array of strings | The distinct utm_medium values, collected the same way. |
utm_source, utm_medium, utm_term, utm_content | string | The stored campaign's defaults. Empty for an unmanaged campaign. |
Campaigns
List campaigns
GET /campaigns
Returns one page of campaigns, stored and found on links together, with the click totals of each and counts for the filter chips.
Permission: link_manager_pro_access (Permissions)
Query parameters:
| Name | Type | Required | Default | What it does |
|---|---|---|---|---|
search | string | No | none | Case-insensitive match on part of the name or the value. |
type | string | No | all | managed lists stored campaigns, virtual lists values found only on links. Any other value gives all. |
source | string | No | none | Keeps campaigns that use this exact utm_source on a link, or as the stored default. |
medium | string | No | none | The same for utm_medium. |
page | integer | No | 1 | The page number. A value below 1 gives page 1. |
per_page | integer | No | 15 | Rows per page. Kept between 1 and 100. A value below 1 gives 1, above 100 gives 100. |
Rows are sorted by total_clicks, highest first, then by slug.
Response: 200. data is a smaller page object than the one in Pagination. It has no from, to or *_url fields.
| Field | Type | What it holds |
|---|---|---|
data | array of objects | The rows of this page, fields in the list row. |
total | integer | Campaigns matching the filters, across all pages. |
current_page | integer | The page returned. |
per_page | integer | Rows per page after the 1 to 100 limit. |
last_page | integer | The last page number, at least 1. |
facets | object | source and medium, each an array of { "value": string, "count": integer }, most used first. Counted after search and type, before source and medium. |
counts | object | all, managed and virtual, each an integer. Counted after search, source and medium, before type. |
bash
curl -u USER:APPLICATION_PASSWORD \
"https://example.com/wp-json/link-manager-pro/v1/campaigns?type=managed&per_page=10"json
{
"success": true,
"data": {
"data": [
{
"slug": "spring-sale",
"name": "Spring sale",
"description": "",
"managed": true,
"link_count": 4,
"affiliate_count": 0,
"total_clicks": 312,
"sources": ["newsletter", "twitter"],
"mediums": ["email", "social"],
"utm_source": "",
"utm_medium": "",
"utm_term": "",
"utm_content": "",
"unique_clicks": 240
}
],
"total": 1,
"current_page": 1,
"per_page": 10,
"last_page": 1,
"facets": {
"source": [{ "value": "newsletter", "count": 1 }],
"medium": [{ "value": "email", "count": 1 }]
},
"counts": { "all": 3, "managed": 1, "virtual": 2 }
}
}Errors: None specific to this route.
List every stored campaign
GET /campaigns/all
Returns all stored campaigns without paging, for a dropdown. Campaigns found only on links are not included.
Permission: link_manager_pro_access (Permissions)
Query parameters: None.
Response: 200. data is an array of stored campaigns, sorted by slug, without link_count. It is [] when none are stored.
json
{
"success": true,
"data": [
{
"id": 5,
"slug": "spring-sale",
"name": "Spring sale",
"description": "",
"utm_source": "",
"utm_medium": "",
"utm_term": "",
"utm_content": ""
}
]
}Errors: None.
List campaign values not yet stored
GET /campaigns/sync/preview
Returns the campaign values found on links, and on partners, that have no stored campaign. Use it to offer "create a campaign from this value".
Permission: link_manager_pro_access (Permissions)
Query parameters: None.
Response: 200. data is an array, most clicks first, [] when every value is stored.
| Field | Type | What it holds |
|---|---|---|
slug | string | The campaign value. |
suggested_name | string | The value with _ and - turned into spaces and each word capitalized. |
link_count | integer | Short links carrying the value. |
total_clicks | integer | Recorded clicks on those links. |
sources | array of strings | The distinct utm_source values. |
mediums | array of strings | The distinct utm_medium values. |
json
{
"success": true,
"data": [
{
"slug": "black-friday",
"suggested_name": "Black Friday",
"link_count": 2,
"total_clicks": 57,
"sources": ["newsletter"],
"mediums": ["email"]
}
]
}Errors: None.
Create a campaign
POST /campaigns
Stores a campaign. To adopt a value found on links, send that value as slug so the campaign matches its links.
Permission: link_manager_pro_access (Permissions)
Body parameters: (JSON)
| Name | Type | Required | Default | What it does |
|---|---|---|---|---|
name | string | Yes | none | The display name. Tags are stripped and the text is trimmed. |
slug | string | No | the name | The campaign value. A value already used on links is kept as written. Any other value goes through sanitize_title(), which gives lower-case letters, digits and hyphens, then URL-decoded. |
description | string | No | empty | Free text. Line breaks are kept. |
utm_source | string | No | empty | Default utm_source for the campaign. |
utm_medium | string | No | empty | Default utm_medium. |
utm_term | string | No | empty | Default utm_term. |
utm_content | string | No | empty | Default utm_content. |
A parameter that is not a string is treated as empty. The code sets no length limit on these fields.
Response: 200, not 201. data is the stored campaign with link_count and affiliate_count. message is "Campaign created successfully."
bash
curl -X POST -u USER:APPLICATION_PASSWORD \
-H "Content-Type: application/json" \
-d '{"name":"Spring sale","utm_medium":"email"}' \
"https://example.com/wp-json/link-manager-pro/v1/campaigns"json
{
"success": true,
"data": {
"id": 5,
"slug": "spring-sale",
"name": "Spring sale",
"description": "",
"utm_source": "",
"utm_medium": "email",
"utm_term": "",
"utm_content": "",
"link_count": 0
},
"message": "Campaign created successfully."
}Errors:
| Status | When |
|---|---|
422 | name is empty: errors.name "Campaign name is required." |
422 | The campaign value is empty, for example a name made only of symbols: errors.slug "Campaign value is required." |
422 | A stored campaign already has this value: errors.slug "A campaign with this value already exists." |
Update a campaign
PUT /campaigns/{campaign}
Replaces a stored campaign. The call writes every field, so a description or utm_* field you leave out is saved empty.
Permission: link_manager_pro_access (Permissions)
Path parameters:
| Name | Type | What it is |
|---|---|---|
campaign | string | The current campaign value, URL-encoded. See Campaign values in the path. |
Body parameters: (JSON)
| Name | Type | Required | Default | What it does |
|---|---|---|---|---|
name | string | Yes | none | The display name. It is required on every update. |
slug | string | No | the current value | The new campaign value. Left out, empty, or equal to the current value (in any spelling) means no rename. |
description | string | No | empty | Replaces the description. Omitted means saved as empty. |
utm_source | string | No | empty | Replaces the default. Omitted means saved as empty. |
utm_medium | string | No | empty | The same. |
utm_term | string | No | empty | The same. |
utm_content | string | No | empty | The same. |
A new slug is normalized like on create. It cannot change while any link uses the current value in its UTM parameters.
Response: 200. data is the stored campaign with link_count and affiliate_count for the value now stored. message is "Campaign updated successfully."
bash
curl -X PUT -u USER:APPLICATION_PASSWORD \
-H "Content-Type: application/json" \
-d '{"name":"Spring sale 2026","description":"Email push","utm_medium":"email"}' \
"https://example.com/wp-json/link-manager-pro/v1/campaigns/spring-sale"json
{
"success": true,
"data": {
"id": 5,
"slug": "spring-sale",
"name": "Spring sale 2026",
"description": "Email push",
"utm_source": "",
"utm_medium": "email",
"utm_term": "",
"utm_content": "",
"link_count": 4
},
"message": "Campaign updated successfully."
}Errors:
| Status | When |
|---|---|
422 | name is empty: errors.name "Campaign name is required." This check runs first, so an unknown campaign with an empty name gets this error. |
422 | The new value is empty: errors.slug "Campaign value is required." |
422 | No stored campaign has the value in the path: errors.slug "Campaign not found." A value that exists only on links is not stored, so it also gets this. |
422 | A rename while links use the current value: errors.slug "Can’t be changed: N link uses it in its UTM tags." (plural for more than one link). |
422 | A rename while affiliates use the current value and no link does: errors.slug "Can’t be changed: N affiliate uses it in its UTM tags." (plural: "N affiliates use it in their UTM tags."). The link check runs first. |
422 | A rename to a value another stored campaign has: errors.slug "A campaign with this value already exists." |
Delete a campaign
DELETE /campaigns/{campaign}
Deletes the stored campaign. Links keep their UTM values, so the value stays in reports as an unmanaged campaign.
Permission: link_manager_pro_access (Permissions)
Path parameters:
| Name | Type | What it is |
|---|---|---|
campaign | string | The campaign value, URL-encoded. |
Response: 200. data is null. message is "Campaign deleted."
json
{
"success": true,
"data": null,
"message": "Campaign deleted."
}Errors:
| Status | When |
|---|---|
404 | No stored campaign has that value, including one that exists only on links. The code is campaign_not_found. |
Campaign reports
A campaign report covers every recorded click on the campaign's short links. None of the report routes except clicks and the CSV export take date parameters, so their numbers are all-time. The /campaigns/{name}/... routes need the campaign value in the path, so the rules for values in the path apply.
Get a campaign report
GET /campaigns/{name}/report
Returns the totals, first and last click and top breakdowns for one campaign, all-time.
Permission: link_manager_pro_access (Permissions)
Path parameters:
| Name | Type | What it is |
|---|---|---|
name | string | The campaign value, URL-encoded. |
Query parameters: None. There is no date range; the report is all-time.
Response: 200. data holds:
| Field | Type | What it holds |
|---|---|---|
slug | string | The campaign value. |
campaign | string | The same value as slug. |
name | string | The stored name, or the value for an unmanaged campaign. |
description | string | The stored description, empty otherwise. |
managed | boolean | Whether a stored campaign exists. |
created_at | string or null | When the stored campaign was created, UTC Y-m-d H:i:s. null for an unmanaged campaign. |
first_click_at | string or null | The earliest recorded click, UTC Y-m-d H:i:s. null without clicks. |
last_click_at | string or null | The latest recorded click, same format. |
link_count | integer | Short links carrying the value. |
affiliate_count | integer | Affiliates whose UTM campaign is this value. |
total_clicks | integer | Recorded clicks on those links. |
unique_clicks | integer | Clicks marked unique. |
top_source | string or null | The tagged utm_source with the most clicks, ties broken alphabetically. If no link with clicks has a source, the first source found on the links, else null. |
sources | array of strings | The distinct utm_source values of the links, in link ID order. |
breakdowns | object | source, medium, device, browser and os. Each is { "items": [{ "label": string, "count": integer }], "total": integer }. items holds at most 10 labels, largest first. total counts all clicks in the breakdown, not only the listed ones. Links without a source or medium count under the label "(not set)". |
utm_source, utm_medium, utm_term, utm_content | string | The stored campaign's defaults. Empty for an unmanaged campaign. |
tracking | object | The site's tracking state, fields below. |
tracking holds:
| Field | Type | What it holds |
|---|---|---|
tracking_enabled | boolean | Whether click tracking is on. |
retention_days | integer | Days clicks are kept, 0 for no limit. |
autolink_site_enabled | boolean | Whether auto-linking is on site-wide. |
history_trusted | boolean | Whether the click history is complete enough to compare periods. |
history_reason | string | Why not, such as tracking_off, retention_short, tracking_resumed, retention_changed, clicks_restored or gap. Empty when trusted. |
bash
curl -u USER:APPLICATION_PASSWORD \
"https://example.com/wp-json/link-manager-pro/v1/campaigns/spring-sale/report"json
{
"success": true,
"data": {
"slug": "spring-sale",
"name": "Spring sale",
"description": "",
"managed": true,
"campaign": "spring-sale",
"created_at": "2026-10-01 09:00:00",
"first_click_at": "2026-10-02 07:41:12",
"last_click_at": "2026-10-22 18:03:09",
"link_count": 4,
"affiliate_count": 0,
"total_clicks": 312,
"unique_clicks": 240,
"top_source": "newsletter",
"sources": ["newsletter", "twitter"],
"breakdowns": {
"source": { "items": [{ "label": "newsletter", "count": 200 }], "total": 312 },
"medium": { "items": [{ "label": "email", "count": 200 }], "total": 312 },
"device": { "items": [{ "label": "Desktop", "count": 180 }], "total": 312 },
"browser": { "items": [{ "label": "Chrome", "count": 150 }], "total": 312 },
"os": { "items": [{ "label": "Windows", "count": 120 }], "total": 312 }
},
"utm_source": "",
"utm_medium": "",
"utm_term": "",
"utm_content": "",
"tracking": {
"tracking_enabled": true,
"retention_days": 0,
"autolink_site_enabled": true,
"history_trusted": true,
"history_reason": ""
}
}
}Errors:
| Status | When |
|---|---|
404 | The value is neither stored, nor on any link, nor used by a partner. The code is campaign_not_found. A value that only a partner uses answers 200 with link_count 0 and managed false. |
Export a campaign report as CSV
GET /campaigns/{name}/report/export
Downloads every recorded click on the campaign's short links as a CSV file, newest first.
Permission: link_manager_pro_access (Permissions)
Path parameters:
| Name | Type | What it is |
|---|---|---|
name | string | The campaign value, URL-encoded. |
Query parameters:
| Name | Type | Required | Default | What it does |
|---|---|---|---|---|
start | string | No | none | First day, Y-m-d in site time. A malformed or impossible date counts as missing. |
end | string | No | none | Last day, Y-m-d. The whole day counts. |
Response: 200. The body is CSV, not JSON, with no envelope. The headers are:
| Header | Value |
|---|---|
Content-Type | text/csv; charset=utf-8 |
Content-Disposition | attachment; filename="link-manager-pro-report-{value}-{Y-m-d}.csv". The value goes through sanitize_file_name(), and the date is today in site time. |
Cache-Control | no-cache, no-store, must-revalidate |
The first row is a header row. Each later row is one click. The columns, in this order:
| Column | What it holds |
|---|---|
Clicked At | The click time in site time, Y-m-d H:i:s. |
Link Slug | The slug of the short link that was clicked. |
Target URL | The destination recorded for the click. |
Country | A two-letter country code, or empty. |
Referrer Domain | The referring host, or empty. |
Referrer URL | The referring page, or empty. |
Device | The device type from the user agent. |
OS | The operating system family. |
Browser | The browser family. |
Unique | yes or no. |
Post ID | The post the click came from, or empty. |
UTM Source, UTM Medium, UTM Campaign, UTM Term, UTM Content | The UTM values recorded for the click, or empty. |
A cell that starts with =, +, -, @, a tab or a carriage return gets a leading single quote, so a spreadsheet does not run it as a formula. A stored campaign with no links gives only the header row.
bash
curl -u USER:APPLICATION_PASSWORD -o campaign.csv \
"https://example.com/wp-json/link-manager-pro/v1/campaigns/spring-sale/report/export?start=2026-10-01&end=2026-10-23"text
Clicked At,"Link Slug","Target URL",Country,"Referrer Domain","Referrer URL",Device,OS,Browser,Unique,"Post ID","UTM Source","UTM Medium","UTM Campaign","UTM Term","UTM Content"
"2026-10-22 18:03:09",blue-widget,"https://shop.example.com/?tag=mysite-20",US,,,Desktop,Windows,Chrome,yes,18,newsletter,email,spring-sale,,Errors:
| Status | When |
|---|---|
404 | The value is neither stored, nor on any link, nor used by a partner. The body is the JSON campaign_not_found error. A value only a partner uses answers 200 with an empty report. |
List a campaign's short links
GET /campaigns/{name}/links
Returns one page of the short links that carry the campaign value, the most clicked first.
Permission: link_manager_pro_access (Permissions)
Path parameters:
| Name | Type | What it is |
|---|---|---|
name | string | The campaign value, URL-encoded. |
Query parameters:
| Name | Type | Required | Default | What it does |
|---|---|---|---|---|
page | integer | No | 1 | The page number. A value below 1 gives page 1. |
per_page | integer | No | 10 | Links per page. Kept between 1 and 100. |
Links are sorted by recorded clicks, highest first, then by ID.
Response: 200. With links, data is a page object (Pagination). Each item in data.data has the fields of the short link object (without the list-only fields such as rules_count) and these extra fields:
| Field | Type | What it holds |
|---|---|---|
clicks | integer | Recorded clicks on this link. click_count is the separate lifetime counter. |
url | string | The destination, the same text as target_url. |
utm_source | string | The link's utm_source, empty when none. |
utm_medium | string | The link's utm_medium, empty when none. |
The short_url field uses the affiliate prefix for links to an affiliate's domain, as described under Short links.
With no links, including an unknown campaign, data is only { "data": [], "total": 0, "current_page": 1, "per_page": 10, "last_page": 1 }. current_page and per_page repeat the request.
json
{
"success": true,
"data": {
"current_page": 1,
"data": [
{
"id": 42,
"slug": "blue-widget",
"target_url": "https://example.com/products/blue-widget",
"redirect_type": 301,
"utm_params": { "utm_source": "newsletter", "utm_medium": "email", "utm_campaign": "spring-sale" },
"click_count": 128,
"short_url": "https://example.com/link/blue-widget",
"clicks": 120,
"url": "https://example.com/products/blue-widget",
"utm_source": "newsletter",
"utm_medium": "email"
}
],
"last_page": 1,
"per_page": 10,
"total": 1
}
}The example shortens the link fields. A real row has all of them.
Errors: None. An unknown campaign returns the empty page.
Get campaign clicks over time
GET /campaigns/{name}/clicks
Returns one row per day with the total and unique clicks on the campaign's links, for a chart, or the same split by utm_source.
Permission: link_manager_pro_access (Permissions)
Path parameters:
| Name | Type | What it is |
|---|---|---|
name | string | The campaign value, URL-encoded. |
Query parameters:
| Name | Type | Required | Default | What it does |
|---|---|---|---|---|
start | string | No | see below | First day, Y-m-d in site time. |
end | string | No | today | Last day, Y-m-d. Never later than today. |
group | string | No | none | source splits the series by each link's own utm_source. Any other value is ignored. |
With neither start nor end, the range runs from the earlier of the stored campaign's creation day and its first click, up to today. With neither of those, it is the last 30 days. With only end, it covers the 30 days ending on that day. A reversed range is swapped, and a range is capped at 7305 days. An invalid date counts as missing.
Response: 200. Without group, data is an array with one item per day, zero-filled:
| Field | Type | What it holds |
|---|---|---|
date | string | The day, Y-m-d in site time. |
total | integer | Clicks that day. |
unique | integer | Unique clicks that day. |
With group=source, data is { "series": [ ... ] }. Each series item has:
| Field | Type | What it holds |
|---|---|---|
key | string | A utm_source value, "" for links with no source, or "__other" for all remaining sources. |
points | array of objects | Days with date, total and unique, as above. |
The three sources with the most recorded clicks get their own item, and the rest merge into __other. An unknown campaign gives series: [].
bash
curl -u USER:APPLICATION_PASSWORD \
"https://example.com/wp-json/link-manager-pro/v1/campaigns/spring-sale/clicks?start=2026-10-20&end=2026-10-22"json
{
"success": true,
"data": [
{ "date": "2026-10-20", "total": 14, "unique": 11 },
{ "date": "2026-10-21", "total": 9, "unique": 7 },
{ "date": "2026-10-22", "total": 0, "unique": 0 }
]
}Errors: None. An unknown campaign returns zero-filled days for the last 30 days, or an empty series with group=source.
Get a campaign's UTM breakdown
GET /campaigns/{name}/utm-breakdown
Returns the campaign's clicks grouped by the combination of utm_source, utm_medium, utm_term and utm_content on its links, all-time.
Permission: link_manager_pro_access (Permissions)
Path parameters:
| Name | Type | What it is |
|---|---|---|
name | string | The campaign value, URL-encoded. |
Query parameters: None.
Response: 200. data is an array, most clicks first. It is [] when no link carries the value. Each item is one distinct combination:
| Field | Type | What it holds |
|---|---|---|
utm_source | string | The utm_source, empty when none. |
utm_medium | string | The utm_medium, empty when none. |
utm_term | string | The utm_term, empty when none. |
utm_content | string | The utm_content, empty when none. |
link_count | integer | Links with this combination. |
clicks | integer | Recorded clicks on those links. |
unique_clicks | integer | Recorded clicks marked unique on those links. |
json
{
"success": true,
"data": [
{
"utm_source": "newsletter",
"utm_medium": "email",
"utm_term": "",
"utm_content": "header-button",
"link_count": 2,
"clicks": 200,
"unique_clicks": 160
}
]
}Errors: None. An unknown campaign returns [].
Get a campaign's clicks by country
GET /campaigns/{name}/countries
Returns the campaign's recorded clicks grouped by country, all-time, for a map.
Permission: link_manager_pro_access (Permissions)
Path parameters:
| Name | Type | What it is |
|---|---|---|
name | string | The campaign value, URL-encoded. |
Query parameters: None.
Response: 200. data holds:
| Field | Type | What it holds |
|---|---|---|
rows | array of objects | One item per country, most clicks first: code (string, two capital letters) and total (integer). |
total | integer | All recorded clicks on the campaign's links, including those with no known country. |
unresolved | integer | Clicks with an empty, XX or invalid country code. They are not in rows. |
json
{
"success": true,
"data": {
"rows": [
{ "code": "US", "total": 180 },
{ "code": "DE", "total": 60 }
],
"total": 312,
"unresolved": 72
}
}Errors: None. An unknown campaign returns { "rows": [], "total": 0, "unresolved": 0 }.
UTM templates
A UTM template is a saved set of five UTM values with a name. The link form uses it to fill the UTM fields. Templates are rows in link_manager_pro_meta with entity_type utm_template and entity_id 0. The name is the meta_key, so names are unique.
The UTM template object
| Field | Type | What it holds |
|---|---|---|
id | integer | The row ID. It is the {id} in the update and delete paths. |
name | string | The template name. |
utm_source | string | Empty when not set. |
utm_medium | string | Empty when not set. |
utm_campaign | string | Empty when not set. |
utm_term | string | Empty when not set. |
utm_content | string | Empty when not set. |
List UTM templates
GET /utm-templates
Returns every saved UTM template, oldest first.
Permission: link_manager_pro_access (Permissions)
Query parameters: None. There is no paging.
Response: 200. data is an array of UTM templates, [] when none.
json
{
"success": true,
"data": [
{
"id": 3,
"name": "Newsletter",
"utm_source": "newsletter",
"utm_medium": "email",
"utm_campaign": "",
"utm_term": "",
"utm_content": ""
}
]
}Errors: None.
Create a UTM template
POST /utm-templates
Saves a new UTM template.
Permission: link_manager_pro_access (Permissions)
Body parameters: (JSON)
| Name | Type | Required | Default | What it does |
|---|---|---|---|---|
name | string | Yes | none | The template name, trimmed and cleaned as plain text. It must be unique. |
utm_source | string | No | empty | The utm_source value. |
utm_medium | string | No | empty | The utm_medium value. |
utm_campaign | string | No | empty | The utm_campaign value. |
utm_term | string | No | empty | The utm_term value. |
utm_content | string | No | empty | The utm_content value. |
Response: 200, not 201. data.template is the UTM template. message is "UTM template saved."
bash
curl -X POST -u USER:APPLICATION_PASSWORD \
-H "Content-Type: application/json" \
-d '{"name":"Newsletter","utm_source":"newsletter","utm_medium":"email"}' \
"https://example.com/wp-json/link-manager-pro/v1/utm-templates"json
{
"success": true,
"data": {
"template": {
"id": 3,
"name": "Newsletter",
"utm_source": "newsletter",
"utm_medium": "email",
"utm_campaign": "",
"utm_term": "",
"utm_content": ""
}
},
"message": "UTM template saved."
}Errors:
| Status | When |
|---|---|
422 | name is empty: errors.name "Template name is required." |
422 | Another template has this name: errors.name "A template with this name already exists." |
Update a UTM template
PUT /utm-templates/{id}
Replaces a template. Every field is written, so a utm_* field you leave out is saved empty.
Permission: link_manager_pro_access (Permissions)
Path parameters:
| Name | Type | What it is |
|---|---|---|
id | integer | The template ID. |
Body parameters: (JSON) The same as Create a UTM template. name is required again.
Response: 200. data.template is the updated UTM template. message is "UTM template updated."
bash
curl -X PUT -u USER:APPLICATION_PASSWORD \
-H "Content-Type: application/json" \
-d '{"name":"Newsletter","utm_source":"newsletter","utm_medium":"email","utm_campaign":"spring-sale"}' \
"https://example.com/wp-json/link-manager-pro/v1/utm-templates/3"json
{
"success": true,
"data": {
"template": {
"id": 3,
"name": "Newsletter",
"utm_source": "newsletter",
"utm_medium": "email",
"utm_campaign": "spring-sale",
"utm_term": "",
"utm_content": ""
}
},
"message": "UTM template updated."
}Errors:
| Status | When |
|---|---|
404 | No template has that ID. The code is not_found. This check runs before validation. |
422 | name is empty: errors.name "Template name is required." |
422 | Another template has this name: errors.name "A template with this name already exists." |
Delete a UTM template
DELETE /utm-templates/{id}
Deletes a saved template. Links that used its values keep them.
Permission: link_manager_pro_access (Permissions)
Path parameters:
| Name | Type | What it is |
|---|---|---|
id | integer | The template ID. |
Response: 200. data is null. message is "UTM template deleted."
json
{
"success": true,
"data": null,
"message": "UTM template deleted."
}Errors:
| Status | When |
|---|---|
404 | No template has that ID. The code is not_found. |