API reference
Every endpoint an API key can call — parameters, fields, answers and error codes — generated from the OpenAPI file.
Every call takes Authorization: Bearer utmk_… and answers { "data": …, "error": null }. How keys, limits, paging and errors work is on Using the API. The machine-readable file for Postman or a code generator is app.utmcap.com/api/v1/openapi.json.
#Campaigns
Campaigns, their flows, and the tools around them.
#GET /campaigns
List campaigns, with figures for the window.
Pinned campaigns first, then newest.
| Parameter | Type | Required | Notes |
|---|---|---|---|
from |
string | Start of the window, UTC. Default 6 days ago. | |
to |
string | End of the window, UTC. Default today. | |
limit |
integer | 1–500, default 500. | |
cursor |
string | next_cursor from the previous page. |
Answer — data has range, rows, next_cursor.
curl "https://app.utmcap.com/api/v1/campaigns" \
-H "Authorization: Bearer $UTMCAP_KEY"
#POST /campaigns
Create a campaign.
Send streams for a full flow, or offer_id / lander_id for a one-path flow. On the shared link domain the alias is replaced with a generated one.
Body
| Field | Type | Required | Notes |
|---|---|---|---|
name |
string | yes | |
alias |
string | yes | 6–64 characters: letters, digits, _ and -, starting with a letter or digit. |
domain_id |
uuid | A tracking domain on this account, or the shared link domain. | |
source_id |
uuid | A traffic source on this account. | |
status |
active, paused |
Default active. | |
streams |
object[] | ||
offer_id |
uuid | For a one-path flow, when streams is not sent. | |
lander_id |
uuid | For a one-path flow, when streams is not sent. | |
mode |
affiliate, lead, store |
lead and store need the goal tracking plan feature (402 plan_limit). | |
destination_url |
string | http(s), up to 2000 characters; empty clears it. | |
primary_goal_key |
string | Lower case, /^[a-z][a-z0-9_]{1,63}$/; empty clears it. | |
measurement_site_id |
string | A site id (UUID), or empty. | |
tracked_goal_keys |
string[] | Up to 50; invalid keys are dropped. | |
cost_model |
auto, cpc, cpuc, cpm, cpa, cps, revshare, manual |
||
cost_value |
number | 0 or more. | |
currency |
string | Three letters, e.g. USD. | |
flow_rotation |
position, weight |
||
bind_visitors |
none, streams, streams_landers, streams_landers_offers |
||
uniqueness_method |
ip, ip_ua, cookie, param |
param needs uniqueness_param. | |
uniqueness_param |
string | Up to 100 characters. | |
uniqueness_ttl_hours |
integer | 1–8760. | |
traffic_loss_pct |
number | 0 up to, not including, 100. | |
group_name |
string | Up to 255 characters. | |
country |
string | Two letters, or empty. | |
tags |
string[] | Up to 20. | |
is_pinned |
boolean | ||
notes |
string | Up to 10000 characters. | |
fallback_url |
string | http(s), or empty. |
Answer (201) — data has id, name, alias, status, streams, url.
Errors besides the usual: plan_limit 402 (The plan campaign limit is reached, or lead/store without goal tracking); bad_body 400 (Body is not a JSON object); duplicate_name 409 (A campaign with this name exists); name_required 400 (name is empty); invalid_alias 400 (alias has the wrong shape); invalid_domain 400 (domain_id is not a domain on this account); invalid_source 400 (source_id is not a source on this account); alias_taken 409 (The alias is in use on that domain); invalid_status 400 (status is not active or paused); buyer_only 403 (A contributor role (not a key) cannot do this); invalid_mode 400 (Unknown mode); invalid_destination_url 400 (A destination url is not http(s)); invalid_goal_key 400 (primary_goal_key has the wrong shape); invalid_site 400 (measurement_site_id is not a UUID); invalid_cost_model 400 (Unknown cost_model); invalid_cost_value 400 (cost_value below 0); invalid_currency 400 (currency is not three letters); invalid_flow_rotation 400 (Unknown flow_rotation); invalid_bind_visitors 400 (Unknown bind_visitors); invalid_uniqueness_method 400 (Unknown uniqueness_method); uniqueness_param_required 400 (uniqueness_method param without uniqueness_param); invalid_uniqueness_param 400 (uniqueness_param too long); invalid_uniqueness_ttl 400 (uniqueness_ttl_hours outside 1–8760); invalid_traffic_loss 400 (traffic_loss_pct outside 0–99.99); invalid_country 400 (country is not two letters); invalid_fallback_url 400 (fallback_url is not http(s)); no_streams 400 (No paths given); duplicate_rule_name 400 (Two rules share a name); duplicate_path_name 400 (Two paths share a name); invalid_action 400 (Unknown action); invalid_stream_type 400 (Unknown stream_type); invalid_transition 400 (Unknown transition); invalid_rule_group 400 (rule_group outside 0–999); invalid_cap 400 (daily_cap below 1); destination_required 400 (A path other than "nothing" has no destination); lander_required 400 (A lander path has no lander destination); invalid_destination 400 (A destination has both id and url, or names an offer or lander not on this account); zero_weights 400 (Every destination weight is zero); invalid_filter 400 (A filter has an unknown attribute or operator, or no value); zero_path_weights 400 (Every path weight is zero); multiple_defaults 400 (More than one default path); read_only_key 403.
curl -X POST "https://app.utmcap.com/api/v1/campaigns" \
-H "Authorization: Bearer $UTMCAP_KEY" \
-H "Content-Type: application/json" \
-d '{ … }'
#GET /campaigns/{id}
One campaign, with its flow.
Answer — data has networks, campaign, streams.
Errors besides the usual: invalid_id 400 (id is not a UUID); not_found 404 (No such campaign on this account).
curl "https://app.utmcap.com/api/v1/campaigns/YOUR_ID" \
-H "Authorization: Bearer $UTMCAP_KEY"
#PATCH /campaigns/{id}
Change a campaign’s settings.
Send only the fields to change. To change the flow, use PUT /campaigns/{id}/flow.
Body
| Field | Type | Required | Notes |
|---|---|---|---|
name |
string | ||
alias |
string | An unchanged alias always passes; a new one follows the create rules. | |
status |
active, paused, archived |
||
domain_id |
string or null | Null or empty clears it. | |
source_id |
string or null | Null or empty clears it. | |
mode |
affiliate, lead, store |
lead and store need the goal tracking plan feature (402 plan_limit). | |
destination_url |
string | http(s), up to 2000 characters; empty clears it. | |
primary_goal_key |
string | Lower case, /^[a-z][a-z0-9_]{1,63}$/; empty clears it. | |
measurement_site_id |
string | A site id (UUID), or empty. | |
tracked_goal_keys |
string[] | Up to 50; invalid keys are dropped. | |
cost_model |
auto, cpc, cpuc, cpm, cpa, cps, revshare, manual |
||
cost_value |
number | 0 or more. | |
currency |
string | Three letters, e.g. USD. | |
flow_rotation |
position, weight |
||
bind_visitors |
none, streams, streams_landers, streams_landers_offers |
||
uniqueness_method |
ip, ip_ua, cookie, param |
param needs uniqueness_param. | |
uniqueness_param |
string | Up to 100 characters. | |
uniqueness_ttl_hours |
integer | 1–8760. | |
traffic_loss_pct |
number | 0 up to, not including, 100. | |
group_name |
string | Up to 255 characters. | |
country |
string | Two letters, or empty. | |
tags |
string[] | Up to 20. | |
is_pinned |
boolean | ||
notes |
string | Up to 10000 characters. | |
fallback_url |
string | http(s), or empty. |
Answer — data has id, name, alias, status.
Errors besides the usual: invalid_id 400 (id is not a UUID); bad_body 400 (Body is not a JSON object); name_required 400 (name is empty); duplicate_name 409 (Another campaign has this name); invalid_alias 400 (alias has the wrong shape); shared_alias 400 (A chosen alias on the shared link domain); invalid_status 400 (Unknown status); invalid_domain 400 (domain_id is not a domain on this account); invalid_source 400 (source_id is not a source on this account); no_fields 400 (Nothing to change); not_found 404 (No such campaign on this account); alias_taken 409 (The alias is in use on that domain); plan_limit 402 (lead/store without goal tracking); invalid_mode 400 (Unknown mode); invalid_destination_url 400 (destination_url is not http(s) or too long); invalid_goal_key 400 (primary_goal_key has the wrong shape); invalid_site 400 (measurement_site_id is not a UUID); invalid_cost_model 400 (Unknown cost_model); invalid_cost_value 400 (cost_value below 0); invalid_currency 400 (currency is not three letters); invalid_flow_rotation 400 (Unknown flow_rotation); invalid_bind_visitors 400 (Unknown bind_visitors); invalid_uniqueness_method 400 (Unknown uniqueness_method); uniqueness_param_required 400 (uniqueness_method param without uniqueness_param); invalid_uniqueness_param 400 (uniqueness_param too long); invalid_uniqueness_ttl 400 (uniqueness_ttl_hours outside 1–8760); invalid_traffic_loss 400 (traffic_loss_pct outside 0–99.99); invalid_country 400 (country is not two letters); invalid_fallback_url 400 (fallback_url is not http(s)); read_only_key 403.
curl -X PATCH "https://app.utmcap.com/api/v1/campaigns/YOUR_ID" \
-H "Authorization: Bearer $UTMCAP_KEY" \
-H "Content-Type: application/json" \
-d '{ … }'
#DELETE /campaigns/{id}
Delete a campaign.
Its link stops answering at once, and the campaign goes to the bin. It can be put back with POST /campaigns/bin until the window passes, after which it is removed with its paths. Recorded clicks are untouched either way.
Answer — data has id, deleted, binned_days.
Errors besides the usual: invalid_id 400 (id is not a UUID); not_found 404 (No such campaign on this account); read_only_key 403.
curl -X DELETE "https://app.utmcap.com/api/v1/campaigns/YOUR_ID" \
-H "Authorization: Bearer $UTMCAP_KEY"
#GET /bin
What is in the bin.
Deleted things, waiting. kind is campaigns, offers, landers, traffic_sources or flows; campaigns by default. Only campaigns are cleared on a timer; the rest wait until the bin is emptied, and days is null for those.
| Parameter | Type | Required | Notes |
|---|---|---|---|
kind |
campaigns, offers, landers, traffic_sources, flows |
Answer — data has kind, rows, days.
Errors besides the usual: invalid_kind 400 (No such kind has a bin).
curl "https://app.utmcap.com/api/v1/bin" \
-H "Authorization: Bearer $UTMCAP_KEY"
#POST /bin
Restore something from the bin.
A restored campaign answers again from this moment. A binned campaign does not hold its path, so if another has taken it since, this is refused and names the one in the way.
Body
| Field | Type | Required | Notes |
|---|---|---|---|
id |
uuid | yes | |
kind |
string | campaigns by default. |
Answer — data has id, name, restored.
Errors besides the usual: invalid_id 400 (id is not a UUID); invalid_kind 400 (No such kind has a bin); alias_taken 409 (A live campaign is on its path); not_found 404 (Not in the bin); read_only_key 403.
curl -X POST "https://app.utmcap.com/api/v1/bin" \
-H "Authorization: Bearer $UTMCAP_KEY" \
-H "Content-Type: application/json" \
-d '{ … }'
#DELETE /bin
Empty the bin, or one thing in it.
Irreversible: a campaign’s paths go with it. Send an id for one, or all=true for the whole bin — which for a buyer means only what they created. Recorded clicks stay, and their names are kept, so past reports still read.
Body
| Field | Type | Required | Notes |
|---|---|---|---|
id |
uuid | yes | |
all |
boolean | yes | Empty the whole bin instead. |
kind |
string | campaigns by default. |
Answer — data has removed.
Errors besides the usual: invalid_id 400 (id is not a UUID); invalid_kind 400 (No such kind has a bin); not_found 404 (Not in the bin); read_only_key 403.
curl -X DELETE "https://app.utmcap.com/api/v1/bin" \
-H "Authorization: Bearer $UTMCAP_KEY" \
-H "Content-Type: application/json" \
-d '{ … }'
#POST /campaigns/{id}/clone
Copy a campaign.
The copy is paused, gets a new alias and is named "… (copy)". Its flow is checked like one sent by hand, so a flow naming an offer deleted since is refused.
Answer (201) — data has id, name, alias, status, paths.
Errors besides the usual: invalid_id 400 (id is not a UUID); plan_limit 402 (The plan campaign limit is reached, or lead/store without goal tracking); not_found 404 (No such campaign on this account); read_only_key 403.
curl -X POST "https://app.utmcap.com/api/v1/campaigns/YOUR_ID/clone" \
-H "Authorization: Bearer $UTMCAP_KEY"
#PUT /campaigns/{id}/flow
Replace a campaign’s flow.
Body
| Field | Type | Required | Notes |
|---|---|---|---|
streams |
object[] | yes |
Answer — data has campaign_id, streams.
Errors besides the usual: invalid_id 400 (id is not a UUID); not_found 404 (No such campaign on this account); no_streams 400 (No paths given); duplicate_rule_name 400 (Two rules share a name); duplicate_path_name 400 (Two paths share a name); invalid_action 400 (Unknown action); invalid_stream_type 400 (Unknown stream_type); invalid_transition 400 (Unknown transition); invalid_rule_group 400 (rule_group outside 0–999); invalid_cap 400 (daily_cap below 1); destination_required 400 (A path other than "nothing" has no destination); lander_required 400 (A lander path has no lander destination); invalid_destination 400 (A destination has both id and url, or names an offer or lander not on this account); invalid_destination_url 400 (A destination url is not http(s)); zero_weights 400 (Every destination weight is zero); invalid_filter 400 (A filter has an unknown attribute or operator, or no value); zero_path_weights 400 (Every path weight is zero); multiple_defaults 400 (More than one default path); read_only_key 403.
curl -X PUT "https://app.utmcap.com/api/v1/campaigns/YOUR_ID/flow" \
-H "Authorization: Bearer $UTMCAP_KEY" \
-H "Content-Type: application/json" \
-d '{ … }'
#GET /campaigns/{id}/funnel
Funnel from click to sale.
| Parameter | Type | Required | Notes |
|---|---|---|---|
days |
integer | 1–365, default 7. | |
by |
lander, source |
Split the funnel. |
Answer — data has steps, days, mode, primaryGoalKey, goalKeys, split.
Errors besides the usual: not_found 404 (No such campaign on this account).
curl "https://app.utmcap.com/api/v1/campaigns/YOUR_ID/funnel" \
-H "Authorization: Bearer $UTMCAP_KEY"
#GET /campaigns/{id}/stats
Totals for one campaign.
| Parameter | Type | Required | Notes |
|---|---|---|---|
from |
string | Start of the window, UTC. Default 6 days ago. | |
to |
string | End of the window, UTC. Default today. |
Answer — data has range, clicks, cost, conversions, revenue.
Errors besides the usual: invalid_id 400 (id is not a UUID).
curl "https://app.utmcap.com/api/v1/campaigns/YOUR_ID/stats" \
-H "Authorization: Bearer $UTMCAP_KEY"
#DELETE /campaigns/{id}/stats
Clear a campaign’s clicks and conversions in the window.
Irreversible. The window is always applied: without from/to it is the last 7 days.
| Parameter | Type | Required | Notes |
|---|---|---|---|
from |
string | Start of the window, UTC. Default 6 days ago. | |
to |
string | End of the window, UTC. Default today. |
Answer — data has id, range, clicks, conversions.
Errors besides the usual: invalid_id 400 (id is not a UUID); not_found 404 (No such campaign on this account); read_only_key 403.
curl -X DELETE "https://app.utmcap.com/api/v1/campaigns/YOUR_ID/stats" \
-H "Authorization: Bearer $UTMCAP_KEY"
#GET /campaigns/activity
Clicks and goals in the last 7 days, by alias.
| Parameter | Type | Required | Notes |
|---|---|---|---|
alias |
string | yes |
Answer — data has clicks, goals.
Errors besides the usual: no_alias 400 (alias is missing).
curl "https://app.utmcap.com/api/v1/campaigns/activity" \
-H "Authorization: Bearer $UTMCAP_KEY"
#GET /campaigns/arrivals
How visitors to a campaign were identified.
| Parameter | Type | Required | Notes |
|---|---|---|---|
alias |
string | ||
days |
integer | 1–365, default 7. |
Answer — data has arrivals.
curl "https://app.utmcap.com/api/v1/campaigns/arrivals" \
-H "Authorization: Bearer $UTMCAP_KEY"
#GET /tools/routing
Every campaign’s rules, paths and destinations.
| Parameter | Type | Required | Notes |
|---|---|---|---|
from |
string | Start of the window, UTC. Default 6 days ago. | |
to |
string | End of the window, UTC. Default today. |
Answer — data has range, campaigns.
curl "https://app.utmcap.com/api/v1/tools/routing" \
-H "Authorization: Bearer $UTMCAP_KEY"
#GET /groups
Group names in use.
| Parameter | Type | Required | Notes |
|---|---|---|---|
scope |
campaigns, offers, landers, domains |
Default campaigns. |
Answer — data has scope, groups.
Errors besides the usual: invalid_scope 400 (Unknown scope).
curl "https://app.utmcap.com/api/v1/groups" \
-H "Authorization: Bearer $UTMCAP_KEY"
#Flows
Saved flows a campaign can start from.
#GET /flows
List saved flows.
Most recently changed first.
| Parameter | Type | Required | Notes |
|---|---|---|---|
limit |
integer | 1–500, default 500. | |
cursor |
string | next_cursor from the previous page. |
Answer — data has rows, next_cursor.
curl "https://app.utmcap.com/api/v1/flows" \
-H "Authorization: Bearer $UTMCAP_KEY"
#POST /flows
Save a flow.
Body
| Field | Type | Required | Notes |
|---|---|---|---|
name |
string | yes | Up to 255 characters. |
paths |
object[] | yes | |
country |
string | Two letters. | |
group_name |
string | Up to 255 characters. | |
tags |
string[] | Up to 20. | |
notes |
string | Up to 2000 characters. |
Answer (201) — data has id, name, country, group, tags, notes, status, paths, rule_count, path_count, destination_count, created_at, updated_at.
Errors besides the usual: bad_body 400 (Body is not a JSON object); required 400 (name is missing); too_long 400 (name is over 255 characters); invalid_country 400 (country is not two letters); no_streams 400 (No paths given); duplicate_rule_name 400 (Two rules share a name); duplicate_path_name 400 (Two paths share a name); invalid_action 400 (Unknown action); invalid_stream_type 400 (Unknown stream_type); invalid_transition 400 (Unknown transition); invalid_rule_group 400 (rule_group outside 0–999); invalid_cap 400 (daily_cap below 1); destination_required 400 (A path other than "nothing" has no destination); lander_required 400 (A lander path has no lander destination); invalid_destination 400 (A destination has both id and url, or names an offer or lander not on this account); invalid_destination_url 400 (A destination url is not http(s)); zero_weights 400 (Every destination weight is zero); invalid_filter 400 (A filter has an unknown attribute or operator, or no value); zero_path_weights 400 (Every path weight is zero); multiple_defaults 400 (More than one default path); read_only_key 403.
curl -X POST "https://app.utmcap.com/api/v1/flows" \
-H "Authorization: Bearer $UTMCAP_KEY" \
-H "Content-Type: application/json" \
-d '{ … }'
#PUT /flows
Replace a saved flow.
Body
| Field | Type | Required | Notes |
|---|---|---|---|
id |
uuid | yes | |
name |
string | yes | Up to 255 characters. |
paths |
object[] | yes | |
country |
string | Two letters. | |
group_name |
string | Up to 255 characters. | |
tags |
string[] | Up to 20. | |
notes |
string | Up to 2000 characters. |
Answer — data has id, name, country, group, tags, notes, status, paths, rule_count, path_count, destination_count, created_at, updated_at.
Errors besides the usual: bad_body 400 (Body is not a JSON object); required 400 (name is missing); too_long 400 (name is over 255 characters); invalid_country 400 (country is not two letters); no_streams 400 (No paths given); duplicate_rule_name 400 (Two rules share a name); duplicate_path_name 400 (Two paths share a name); invalid_action 400 (Unknown action); invalid_stream_type 400 (Unknown stream_type); invalid_transition 400 (Unknown transition); invalid_rule_group 400 (rule_group outside 0–999); invalid_cap 400 (daily_cap below 1); destination_required 400 (A path other than "nothing" has no destination); lander_required 400 (A lander path has no lander destination); invalid_destination 400 (A destination has both id and url, or names an offer or lander not on this account); invalid_destination_url 400 (A destination url is not http(s)); zero_weights 400 (Every destination weight is zero); invalid_filter 400 (A filter has an unknown attribute or operator, or no value); zero_path_weights 400 (Every path weight is zero); multiple_defaults 400 (More than one default path); invalid_id 400 (id is not a UUID); not_found 404 (No such flow); read_only_key 403.
curl -X PUT "https://app.utmcap.com/api/v1/flows" \
-H "Authorization: Bearer $UTMCAP_KEY" \
-H "Content-Type: application/json" \
-d '{ … }'
#PATCH /flows
Pause or resume a saved flow.
Body
| Field | Type | Required | Notes |
|---|---|---|---|
id |
uuid | yes | |
is_active |
boolean | yes |
Answer — data has id, status.
Errors besides the usual: invalid_id 400 (id is not a UUID); invalid_status 400 (is_active is not true or false); not_found 404 (No such flow); read_only_key 403.
curl -X PATCH "https://app.utmcap.com/api/v1/flows" \
-H "Authorization: Bearer $UTMCAP_KEY" \
-H "Content-Type: application/json" \
-d '{ … }'
#DELETE /flows
Delete a saved flow.
| Parameter | Type | Required | Notes |
|---|---|---|---|
id |
uuid | yes |
Answer — data has id, deleted.
Errors besides the usual: invalid_id 400 (id is not a UUID); not_found 404 (No such flow); read_only_key 403.
curl -X DELETE "https://app.utmcap.com/api/v1/flows" \
-H "Authorization: Bearer $UTMCAP_KEY"
#Rules
Saved rules a flow path can use.
#GET /rules
List saved rules.
By name.
| Parameter | Type | Required | Notes |
|---|---|---|---|
limit |
integer | 1–500, default 500. | |
cursor |
string | next_cursor from the previous page. |
Answer — data has rows, next_cursor.
curl "https://app.utmcap.com/api/v1/rules" \
-H "Authorization: Bearer $UTMCAP_KEY"
#POST /rules
Save a rule.
Body
| Field | Type | Required | Notes |
|---|---|---|---|
name |
string | yes | Up to 255 characters. |
notes |
string | Up to 2000 characters. | |
logic |
and, or |
Anything but "or" is and. | |
conditions |
object[] | yes | The first 20 are used. Each value item is cut to 200 characters; up to 200 items. |
Answer (201) — data has id, name, notes, logic, conditions, condition_count, created_at, updated_at.
Errors besides the usual: bad_body 400 (Body is not a JSON object); required 400 (name is missing); too_long 400 (name is over 255 characters); invalid_condition 400 (A condition has an unknown attribute or operator, or no value); no_conditions 400 (No conditions); duplicate_name 400 (A rule with this name exists); read_only_key 403.
curl -X POST "https://app.utmcap.com/api/v1/rules" \
-H "Authorization: Bearer $UTMCAP_KEY" \
-H "Content-Type: application/json" \
-d '{ … }'
#PUT /rules
Replace a saved rule.
Body
| Field | Type | Required | Notes |
|---|---|---|---|
id |
uuid | yes | |
name |
string | yes | Up to 255 characters. |
notes |
string | yes | Up to 2000 characters. |
logic |
and, or |
yes | Anything but "or" is and. |
conditions |
object[] | yes | The first 20 are used. Each value item is cut to 200 characters; up to 200 items. |
Answer — data has id, name, notes, logic, conditions, condition_count, created_at, updated_at.
Errors besides the usual: bad_body 400 (Body is not a JSON object); required 400 (name is missing); too_long 400 (name is over 255 characters); invalid_condition 400 (A condition has an unknown attribute or operator, or no value); no_conditions 400 (No conditions); duplicate_name 400 (A rule with this name exists); invalid_id 400 (id is not a UUID); not_found 404 (No such rule); read_only_key 403.
curl -X PUT "https://app.utmcap.com/api/v1/rules" \
-H "Authorization: Bearer $UTMCAP_KEY" \
-H "Content-Type: application/json" \
-d '{ … }'
#DELETE /rules
Delete a saved rule.
| Parameter | Type | Required | Notes |
|---|---|---|---|
id |
uuid | yes |
Answer — data has id, deleted.
Errors besides the usual: invalid_id 400 (id is not a UUID); not_found 404 (No such rule); read_only_key 403.
curl -X DELETE "https://app.utmcap.com/api/v1/rules" \
-H "Authorization: Bearer $UTMCAP_KEY"
#Offers
Where a campaign sends a visitor to convert.
#GET /offers
List offers, with figures for the window.
| Parameter | Type | Required | Notes |
|---|---|---|---|
from |
string | Start of the window, UTC. Default 6 days ago. | |
to |
string | End of the window, UTC. Default today. | |
limit |
integer | 1–500, default 500. | |
cursor |
string | next_cursor from the previous page. |
Answer — data has range, rows, next_cursor.
curl "https://app.utmcap.com/api/v1/offers" \
-H "Authorization: Bearer $UTMCAP_KEY"
#POST /offers
Create an offer.
Body
| Field | Type | Required | Notes |
|---|---|---|---|
name |
string | yes | Up to 255 characters. |
url |
string | yes | http(s); {token} placeholders allowed. |
network_id |
uuid | An affiliate network on this account. | |
payout |
number | 0 to 1,000,000,000. Default 0. | |
payout_type |
fixed, percent |
Default fixed. | |
currency |
string | Three letters; anything else is read as USD. | |
country |
string | Two letters, or empty. | |
daily_cap |
number or null | Conversions a day, 1–1,000,000. | |
notes |
string | Up to 2000 characters. | |
group_name |
string | Up to 255 characters. | |
payout_from_param |
boolean | Take the payout from the postback. Default true. | |
tags |
string[] | Up to 20. |
Answer (201) — data has The saved row: id, name, url, payout, currency, group_name and group (the same value), daily_cap, payout_from_param, tags..
Errors besides the usual: bad_body 400 (Body is not a JSON object); required 400 (A required field is missing); too_long 400 (A field is too long); invalid_url 400 (A URL is not http(s)); invalid_number 400 (A number field is not a number); out_of_range 400 (A number is outside its range); duplicate_name 409 (The name is in use); invalid_network 400 (network_id is not a network on this account); invalid_country 400 (country is not two letters); plan_limit 402 (The plan offers limit is reached); read_only_key 403.
curl -X POST "https://app.utmcap.com/api/v1/offers" \
-H "Authorization: Bearer $UTMCAP_KEY" \
-H "Content-Type: application/json" \
-d '{ … }'
#PUT /offers
Replace an offer.
Every field is written; one left out goes back to its default.
Body
| Field | Type | Required | Notes |
|---|---|---|---|
id |
uuid | yes | |
name |
string | yes | Up to 255 characters. |
url |
string | yes | http(s); {token} placeholders allowed. |
network_id |
uuid | An affiliate network on this account. | |
payout |
number | 0 to 1,000,000,000. Default 0. | |
payout_type |
fixed, percent |
Default fixed. | |
currency |
string | Three letters; anything else is read as USD. | |
country |
string | Two letters, or empty. | |
daily_cap |
number or null | Conversions a day, 1–1,000,000. | |
notes |
string | Up to 2000 characters. | |
group_name |
string | Up to 255 characters. | |
payout_from_param |
boolean | Take the payout from the postback. Default true. | |
tags |
string[] | Up to 20. |
Answer — data has The saved row: id, name, url, payout, currency, group_name and group (the same value), daily_cap, payout_from_param, tags..
Errors besides the usual: bad_body 400 (Body is not a JSON object); required 400 (A required field is missing); too_long 400 (A field is too long); invalid_url 400 (A URL is not http(s)); invalid_number 400 (A number field is not a number); out_of_range 400 (A number is outside its range); duplicate_name 409 (The name is in use); invalid_network 400 (network_id is not a network on this account); invalid_country 400 (country is not two letters); invalid_id 400 (id is not a UUID); not_found 404 (Not on this account); read_only_key 403.
curl -X PUT "https://app.utmcap.com/api/v1/offers" \
-H "Authorization: Bearer $UTMCAP_KEY" \
-H "Content-Type: application/json" \
-d '{ … }'
#PATCH /offers
Pause or resume an offer.
Body
| Field | Type | Required | Notes |
|---|---|---|---|
id |
uuid | yes | |
is_active |
boolean | yes |
Answer — data has id, status.
Errors besides the usual: invalid_id 400 (id is not a UUID); invalid_status 400 (is_active is not true or false); not_found 404 (Not on this account); read_only_key 403.
curl -X PATCH "https://app.utmcap.com/api/v1/offers" \
-H "Authorization: Bearer $UTMCAP_KEY" \
-H "Content-Type: application/json" \
-d '{ … }'
#DELETE /offers
Delete an offer.
Kept for its history; no longer listed.
| Parameter | Type | Required | Notes |
|---|---|---|---|
id |
uuid | yes |
Answer — data has id, deactivated.
Errors besides the usual: invalid_id 400 (id is not a UUID); not_found 404 (Not on this account); read_only_key 403.
curl -X DELETE "https://app.utmcap.com/api/v1/offers" \
-H "Authorization: Bearer $UTMCAP_KEY"
#POST /offers/{id}/clone
Copy an offer.
Answer (201) — data has The copy, named "… (copy)"..
Errors besides the usual: invalid_id 400 (id is not a UUID); not_found 404 (Not on this account); read_only_key 403.
curl -X POST "https://app.utmcap.com/api/v1/offers/YOUR_ID/clone" \
-H "Authorization: Bearer $UTMCAP_KEY"
#Landing pages
Pages a visitor sees before the offer.
#GET /landers
List landing pages, with figures for the window.
| Parameter | Type | Required | Notes |
|---|---|---|---|
from |
string | Start of the window, UTC. Default 6 days ago. | |
to |
string | End of the window, UTC. Default today. | |
limit |
integer | 1–500, default 500. | |
cursor |
string | next_cursor from the previous page. |
Answer — data has range, rows, next_cursor.
curl "https://app.utmcap.com/api/v1/landers" \
-H "Authorization: Bearer $UTMCAP_KEY"
#POST /landers
Create a landing page.
Body
| Field | Type | Required | Notes |
|---|---|---|---|
name |
string | yes | Up to 255 characters. |
url |
string | yes | http(s). |
click_slots |
integer | Call-to-action slots, 1–20. Default 1. | |
group_name |
string | Up to 255 characters. | |
country |
string | Two letters. | |
tags |
string[] | Up to 20. | |
notes |
string | Up to 2000 characters. |
Answer (201) — data has The saved row: id, name, url, click_slots, group_name, country, tags, notes..
Errors besides the usual: bad_body 400 (Body is not a JSON object); required 400 (A required field is missing); too_long 400 (A field is too long); invalid_url 400 (A URL is not http(s)); invalid_number 400 (A number field is not a number); out_of_range 400 (A number is outside its range); duplicate_name 409 (The name is in use); invalid_country 400 (country is not two letters); plan_limit 402 (The plan landing pages limit is reached); read_only_key 403.
curl -X POST "https://app.utmcap.com/api/v1/landers" \
-H "Authorization: Bearer $UTMCAP_KEY" \
-H "Content-Type: application/json" \
-d '{ … }'
#PUT /landers
Replace a landing page.
Body
| Field | Type | Required | Notes |
|---|---|---|---|
id |
uuid | yes | |
name |
string | yes | Up to 255 characters. |
url |
string | yes | http(s). |
click_slots |
integer | Call-to-action slots, 1–20. Default 1. | |
group_name |
string | Up to 255 characters. | |
country |
string | Two letters. | |
tags |
string[] | Up to 20. | |
notes |
string | Up to 2000 characters. |
Answer — data has id.
Errors besides the usual: bad_body 400 (Body is not a JSON object); required 400 (A required field is missing); too_long 400 (A field is too long); invalid_url 400 (A URL is not http(s)); invalid_number 400 (A number field is not a number); out_of_range 400 (A number is outside its range); duplicate_name 409 (The name is in use); invalid_id 400 (id is not a UUID); not_found 404 (Not on this account); invalid_country 400 (country is not two letters); read_only_key 403.
curl -X PUT "https://app.utmcap.com/api/v1/landers" \
-H "Authorization: Bearer $UTMCAP_KEY" \
-H "Content-Type: application/json" \
-d '{ … }'
#PATCH /landers
Pause or resume a landing page, or change its protection.
Send is_active to pause or resume. Otherwise protection is read and saved.
Body
| Field | Type | Required | Notes |
|---|---|---|---|
id |
uuid | yes | |
is_active |
boolean | ||
protection |
object | Page protection: who is shown the page, and where everybody else goes. |
Answer — data has { id, status } or { id, protection }..
Errors besides the usual: invalid_id 400 (id is not a UUID); not_found 404 (Not on this account); invalid_fallback 400 (protection.fallbackUrl is not http(s)); bad_body 400 (Body is not a JSON object); read_only_key 403.
curl -X PATCH "https://app.utmcap.com/api/v1/landers" \
-H "Authorization: Bearer $UTMCAP_KEY" \
-H "Content-Type: application/json" \
-d '{ … }'
#DELETE /landers
Delete a landing page.
| Parameter | Type | Required | Notes |
|---|---|---|---|
id |
uuid | yes |
Answer — data has id, deactivated.
Errors besides the usual: invalid_id 400 (id is not a UUID); not_found 404 (Not on this account); read_only_key 403.
curl -X DELETE "https://app.utmcap.com/api/v1/landers" \
-H "Authorization: Bearer $UTMCAP_KEY"
#POST /landers/{id}/clone
Copy a landing page.
Answer (201) — data has The copy, named "… (copy)"..
Errors besides the usual: invalid_id 400 (id is not a UUID); not_found 404 (Not on this account); read_only_key 403.
curl -X POST "https://app.utmcap.com/api/v1/landers/YOUR_ID/clone" \
-H "Authorization: Bearer $UTMCAP_KEY"
#Traffic sources
Where clicks come from, and their ad-platform integrations.
#GET /traffic-sources
List traffic sources, with figures for the window.
| Parameter | Type | Required | Notes |
|---|---|---|---|
from |
string | Start of the window, UTC. Default 6 days ago. | |
to |
string | End of the window, UTC. Default today. | |
limit |
integer | 1–500, default 500. | |
cursor |
string | next_cursor from the previous page. |
Answer — data has range, rows, next_cursor.
curl "https://app.utmcap.com/api/v1/traffic-sources" \
-H "Authorization: Bearer $UTMCAP_KEY"
#POST /traffic-sources
Create a traffic source.
Body
| Field | Type | Required | Notes |
|---|---|---|---|
name |
string | yes | Up to 255 characters. |
token_macros |
object | Parameter name → the source’s macro, e.g. { "sub1": "{zoneid}" }. | |
cost_param |
string | Up to 100 characters. | |
cost_model |
cpc, cpm, cpa, manual |
Default cpc. | |
postback_url |
string | http(s). | |
external_id_param |
string | Up to 100 characters. | |
notes |
string | Up to 2000 characters. | |
traffic_loss_pct |
number | 0–99.99. | |
group_name |
string | Up to 255 characters. | |
tags |
string[] | Up to 20. | |
cost_macro |
string | Up to 64 characters of macro characters; anything else is dropped. | |
platform |
string | A known platform template, e.g. google_ads; anything else is dropped. |
Answer (201) — data has The saved row: id, name, cost_model, traffic_loss_pct, group_name, tags, cost_macro..
Errors besides the usual: bad_body 400 (Body is not a JSON object); required 400 (A required field is missing); too_long 400 (A field is too long); invalid_url 400 (A URL is not http(s)); invalid_number 400 (A number field is not a number); out_of_range 400 (A number is outside its range); duplicate_name 409 (The name is in use); invalid_value 400 (cost_model is not one of the choices); plan_limit 402 (The plan integrations limit is reached); read_only_key 403.
curl -X POST "https://app.utmcap.com/api/v1/traffic-sources" \
-H "Authorization: Bearer $UTMCAP_KEY" \
-H "Content-Type: application/json" \
-d '{ … }'
#PUT /traffic-sources
Replace a traffic source.
Body
| Field | Type | Required | Notes |
|---|---|---|---|
id |
uuid | yes | |
name |
string | yes | Up to 255 characters. |
token_macros |
object | Parameter name → the source’s macro, e.g. { "sub1": "{zoneid}" }. | |
cost_param |
string | Up to 100 characters. | |
cost_model |
cpc, cpm, cpa, manual |
Default cpc. | |
postback_url |
string | http(s). | |
external_id_param |
string | Up to 100 characters. | |
notes |
string | Up to 2000 characters. | |
traffic_loss_pct |
number | 0–99.99. | |
group_name |
string | Up to 255 characters. | |
tags |
string[] | Up to 20. | |
cost_macro |
string | Up to 64 characters of macro characters; anything else is dropped. | |
platform |
string | A known platform template, e.g. google_ads; anything else is dropped. |
Answer — data has id.
Errors besides the usual: bad_body 400 (Body is not a JSON object); required 400 (A required field is missing); too_long 400 (A field is too long); invalid_url 400 (A URL is not http(s)); invalid_number 400 (A number field is not a number); out_of_range 400 (A number is outside its range); duplicate_name 409 (The name is in use); invalid_value 400 (cost_model is not one of the choices); plan_limit 402 (The plan integrations limit is reached); invalid_id 400 (id is not a UUID); not_found 404 (Not on this account); read_only_key 403.
curl -X PUT "https://app.utmcap.com/api/v1/traffic-sources" \
-H "Authorization: Bearer $UTMCAP_KEY" \
-H "Content-Type: application/json" \
-d '{ … }'
#PATCH /traffic-sources
Pause or resume a traffic source.
Body
| Field | Type | Required | Notes |
|---|---|---|---|
id |
uuid | yes | |
is_active |
boolean | yes |
Answer — data has id, status.
Errors besides the usual: invalid_id 400 (id is not a UUID); invalid_status 400 (is_active is not true or false); not_found 404 (Not on this account); plan_limit 402 (Resuming would pass the plan integrations limit); read_only_key 403.
curl -X PATCH "https://app.utmcap.com/api/v1/traffic-sources" \
-H "Authorization: Bearer $UTMCAP_KEY" \
-H "Content-Type: application/json" \
-d '{ … }'
#DELETE /traffic-sources
Delete a traffic source.
| Parameter | Type | Required | Notes |
|---|---|---|---|
id |
uuid | yes |
Answer — data has id, deactivated.
Errors besides the usual: invalid_id 400 (id is not a UUID); not_found 404 (Not on this account); read_only_key 403.
curl -X DELETE "https://app.utmcap.com/api/v1/traffic-sources" \
-H "Authorization: Bearer $UTMCAP_KEY"
#GET /traffic-sources/{id}/integration
The source’s cost-sync and conversion-upload connection.
Secret credentials come back masked.
Answer — data has configured, provider, is_enabled, cost_endpoint, credentials (masked), last_sync_at, last_status, last_error, conversion settings..
Errors besides the usual: invalid_id 400 (id is not a UUID); not_found 404 (Not on this account).
curl "https://app.utmcap.com/api/v1/traffic-sources/YOUR_ID/integration" \
-H "Authorization: Bearer $UTMCAP_KEY"
#PUT /traffic-sources/{id}/integration
Connect or change the source’s ad-platform integration.
Body
| Field | Type | Required | Notes |
|---|---|---|---|
provider |
string | yes | A supported ad platform or a custom cost endpoint. |
cost_endpoint |
object | For a custom endpoint: url, method, headers, body, rowsPath, dayField, campaignField, spendField, clicksField, impressionsField, spendDivisor, currency. | |
credentials |
object | Field → value. Blank values are left as they were. | |
credentials_clear |
string[] | Credential fields to remove. | |
is_enabled |
boolean | ||
oauth_connection_id |
string | ||
conversion_action |
string | Google Ads: the conversion action id. Microsoft: the offline goal name. | |
conversion_statuses |
approved, pending[] |
||
conversions_enabled |
boolean |
Answer — data has id, saved.
Errors besides the usual: invalid_id 400 (id is not a UUID); not_found 404 (Not on this account); bad_body 400 (Body is not a JSON object); invalid_provider 400 (Unknown provider); invalid_pixel_id 400 (pixel_id is not 5–20 digits); invalid_capi_token 400 (capi_token is too short or has spaces); no_encryption_key 400 (This install cannot store credentials yet); no_url 400 (Enabled without a URL); choose_connection 400 (Several connections; name one); not_connected 400 (The platform is not connected); invalid_conversion_action 400 (conversion_action is not an id or link); no_conversion_action 400 (Uploads switched on without a conversion action); no_capi_credentials 400 (Meta uploads switched on without pixel_id and capi_token); invalid_pixel_code 400 (pixel_code is not 8–40 letters and digits); invalid_events_api_token 400 (events_token is too short or has spaces or symbols); no_events_api_credentials 400 (TikTok uploads switched on without pixel_code and events_token); read_only_key 403.
curl -X PUT "https://app.utmcap.com/api/v1/traffic-sources/YOUR_ID/integration" \
-H "Authorization: Bearer $UTMCAP_KEY" \
-H "Content-Type: application/json" \
-d '{ … }'
#POST /traffic-sources/{id}/integration
Test the connection, or sync spend now.
Body
| Field | Type | Required | Notes |
|---|---|---|---|
action |
test, sync |
Default test. | |
from |
string | YYYY-MM-DD. | |
to |
string | YYYY-MM-DD. |
Answer — data has The test or sync result, with range..
Errors besides the usual: invalid_id 400 (id is not a UUID); not_found 404 (Not on this account); bad_body 400 (Body is not a JSON object); not_configured 400 (No integration saved); no_url 400 (No URL to call); read_only_key 403.
curl -X POST "https://app.utmcap.com/api/v1/traffic-sources/YOUR_ID/integration" \
-H "Authorization: Bearer $UTMCAP_KEY" \
-H "Content-Type: application/json" \
-d '{ … }'
#DELETE /traffic-sources/{id}/integration
Remove the source’s integration.
Answer — data has id, deleted.
Errors besides the usual: invalid_id 400 (id is not a UUID); not_found 404 (Not on this account); read_only_key 403.
curl -X DELETE "https://app.utmcap.com/api/v1/traffic-sources/YOUR_ID/integration" \
-H "Authorization: Bearer $UTMCAP_KEY"
#GET /traffic-sources/{id}/integration/conversion-actions
Conversion actions on the connected Google or Microsoft account.
Answer — data has actions.
Errors besides the usual: invalid_id 400 (id is not a UUID); not_google 400 (The source is not connected to Google Ads or Microsoft Ads); not_connected 400 (No platform connection); credentials 400 (The connection’s credentials failed); no_customer_id 400 (No customer id set); no_account_id 400 (No Microsoft account id set); google_refused 400 (Google refused); microsoft_refused 400 (Microsoft refused).
curl "https://app.utmcap.com/api/v1/traffic-sources/YOUR_ID/integration/conversion-actions" \
-H "Authorization: Bearer $UTMCAP_KEY"
#POST /traffic-sources/{id}/integration/conversion-actions
Create the conversion action UTMCAP uploads to.
Answer — data has The created action. Microsoft adds ready_at: goals accept uploads about two hours after creation..
Errors besides the usual: invalid_id 400 (id is not a UUID); not_google 400 (The source is not connected to Google Ads or Microsoft Ads); not_connected 400 (No platform connection); credentials 400 (The connection’s credentials failed); no_customer_id 400 (No customer id set); no_account_id 400 (No Microsoft account id set); google_refused 400 (Google refused); microsoft_refused 400 (Microsoft refused); read_only_key 403.
curl -X POST "https://app.utmcap.com/api/v1/traffic-sources/YOUR_ID/integration/conversion-actions" \
-H "Authorization: Bearer $UTMCAP_KEY"
#Affiliate networks
Where conversions come from.
#GET /networks
List affiliate networks.
Send from/to to include each network’s raw counts; leave them out for names and settings only.
| Parameter | Type | Required | Notes |
|---|---|---|---|
from |
string | Start of the window, UTC. Default 6 days ago. | |
to |
string | End of the window, UTC. Default today. | |
limit |
integer | 1–500, default 500. | |
cursor |
string | next_cursor from the previous page. |
Answer — data has rows, next_cursor.
curl "https://app.utmcap.com/api/v1/networks" \
-H "Authorization: Bearer $UTMCAP_KEY"
#POST /networks
Create an affiliate network.
Body
| Field | Type | Required | Notes |
|---|---|---|---|
name |
string | yes | Up to 255 characters. |
currency |
string | Three letters; anything else is read as USD. | |
postback_params |
object | The parameter names the network sends, by click_id, payout, status, conversion_id, goal. | |
status_map |
object | Network status words by our status: approved, pending, rejected, chargeback → string[]. | |
postback_secret |
string | Up to 255 characters. On PUT, left unchanged when blank. | |
url |
string | Up to 2000 characters. | |
offer_type |
CPA, CPS, CPL, CPC, CPM |
||
group_name |
string | ||
tags |
string[] | Up to 20. | |
notes |
string | ||
manager_name |
string | ||
manager_email |
string | ||
manager_phone |
string | ||
manager_im |
string | ||
payment_terms |
string | ||
payment_frequency |
string | ||
minimum_payout |
number or null | 0–1,000,000. | |
macros |
object | The network’s own macros, by click_id, payout, status, conversion_id, goal. | |
click_param |
string | The parameter that carries the click id to the network. |
Answer (201) — data has id, name, currency.
Errors besides the usual: bad_body 400 (Body is not a JSON object); required 400 (A required field is missing); too_long 400 (A field is too long); invalid_url 400 (A URL is not http(s)); invalid_number 400 (A number field is not a number); out_of_range 400 (A number is outside its range); duplicate_name 409 (The name is in use); plan_limit 402 (The plan integrations limit is reached); read_only_key 403.
curl -X POST "https://app.utmcap.com/api/v1/networks" \
-H "Authorization: Bearer $UTMCAP_KEY" \
-H "Content-Type: application/json" \
-d '{ … }'
#PUT /networks
Replace an affiliate network.
Body
| Field | Type | Required | Notes |
|---|---|---|---|
id |
uuid | yes | |
name |
string | yes | Up to 255 characters. |
currency |
string | Three letters; anything else is read as USD. | |
postback_params |
object | The parameter names the network sends, by click_id, payout, status, conversion_id, goal. | |
status_map |
object | Network status words by our status: approved, pending, rejected, chargeback → string[]. | |
postback_secret |
string | Up to 255 characters. On PUT, left unchanged when blank. | |
url |
string | Up to 2000 characters. | |
offer_type |
CPA, CPS, CPL, CPC, CPM |
||
group_name |
string | ||
tags |
string[] | Up to 20. | |
notes |
string | ||
manager_name |
string | ||
manager_email |
string | ||
manager_phone |
string | ||
manager_im |
string | ||
payment_terms |
string | ||
payment_frequency |
string | ||
minimum_payout |
number or null | 0–1,000,000. | |
macros |
object | The network’s own macros, by click_id, payout, status, conversion_id, goal. | |
click_param |
string | The parameter that carries the click id to the network. |
Answer — data has id, name, currency.
Errors besides the usual: bad_body 400 (Body is not a JSON object); required 400 (A required field is missing); too_long 400 (A field is too long); invalid_url 400 (A URL is not http(s)); invalid_number 400 (A number field is not a number); out_of_range 400 (A number is outside its range); duplicate_name 409 (The name is in use); invalid_id 400 (id is not a UUID); not_found 404 (Not on this account); plan_limit 402 (The plan integrations limit is reached); read_only_key 403.
curl -X PUT "https://app.utmcap.com/api/v1/networks" \
-H "Authorization: Bearer $UTMCAP_KEY" \
-H "Content-Type: application/json" \
-d '{ … }'
#PATCH /networks
Pause or resume an affiliate network.
Body
| Field | Type | Required | Notes |
|---|---|---|---|
id |
uuid | yes | |
is_active |
boolean | yes |
Answer — data has id, status.
Errors besides the usual: invalid_id 400 (id is not a UUID); invalid_status 400 (is_active is not true or false); not_found 404 (Not on this account); plan_limit 402 (Resuming would pass the plan integrations limit); read_only_key 403.
curl -X PATCH "https://app.utmcap.com/api/v1/networks" \
-H "Authorization: Bearer $UTMCAP_KEY" \
-H "Content-Type: application/json" \
-d '{ … }'
#DELETE /networks
Delete an affiliate network.
| Parameter | Type | Required | Notes |
|---|---|---|---|
id |
uuid | yes |
Answer — data has id, deactivated.
Errors besides the usual: invalid_id 400 (id is not a UUID); not_found 404 (Not on this account); read_only_key 403.
curl -X DELETE "https://app.utmcap.com/api/v1/networks" \
-H "Authorization: Bearer $UTMCAP_KEY"
#GET /networks/{id}/integration
The network’s API connection, for importing conversions.
Answer — data has networkId, networkName, connectedTo, lastStatus, lastError, lastCheckedAt, lastSyncedAt, providers[], encryptionAvailable..
Errors besides the usual: not_found 404 (Not on this account).
curl "https://app.utmcap.com/api/v1/networks/YOUR_ID/integration" \
-H "Authorization: Bearer $UTMCAP_KEY"
#PUT /networks/{id}/integration
Connect the network’s API.
Body
| Field | Type | Required | Notes |
|---|---|---|---|
provider |
string | yes | |
credentials |
object | Field → value. |
Answer — data has saved.
Errors besides the usual: not_found 404 (Not on this account); unknown_provider 400 (Unknown provider); bad_body 400 (Body is not a JSON object); read_only_key 403.
curl -X PUT "https://app.utmcap.com/api/v1/networks/YOUR_ID/integration" \
-H "Authorization: Bearer $UTMCAP_KEY" \
-H "Content-Type: application/json" \
-d '{ … }'
#DELETE /networks/{id}/integration
Disconnect the network’s API.
Answer — data has disconnected.
Errors besides the usual: not_found 404 (Not on this account); read_only_key 403.
curl -X DELETE "https://app.utmcap.com/api/v1/networks/YOUR_ID/integration" \
-H "Authorization: Bearer $UTMCAP_KEY"
#POST /networks/{id}/integration/sync
Import the network’s conversions for a date range.
A dry run unless apply is true.
Body
| Field | Type | Required | Notes |
|---|---|---|---|
from |
string | yes | YYYY-MM-DD. |
to |
string | yes | YYYY-MM-DD. |
apply |
boolean |
Answer — data has dryRun, provider, from, to, total, matched, unmatched, payout, outcomes.
Errors besides the usual: not_found 404 (Not on this account); bad_range 400 (from/to missing or reversed); not_connected 400 (The network is not connected); network_error 502 (The network’s API failed); too_many 400 (Over 5000 orders; use a shorter range); bad_body 400 (Body is not a JSON object); read_only_key 403.
curl -X POST "https://app.utmcap.com/api/v1/networks/YOUR_ID/integration/sync" \
-H "Authorization: Bearer $UTMCAP_KEY" \
-H "Content-Type: application/json" \
-d '{ … }'
#POST /networks/{id}/integration/test
Test the network’s API connection.
Answer — data has ok, provider, message, detail.
Errors besides the usual: not_found 404 (Not on this account); not_connected 400 (The network is not connected); read_only_key 403.
curl -X POST "https://app.utmcap.com/api/v1/networks/YOUR_ID/integration/test" \
-H "Authorization: Bearer $UTMCAP_KEY"
#GET /network-templates
Saved network templates.
Answer — data has rows.
curl "https://app.utmcap.com/api/v1/network-templates" \
-H "Authorization: Bearer $UTMCAP_KEY"
#POST /network-templates
Save network templates.
One template, or { templates: [...] } for up to 1000. Saved by name; an existing name is replaced.
Body
A template, or { templates: [...] }. camelCase or snake_case keys.
Answer (201) — data has saved, skipped, rows.
Errors besides the usual: empty 400 (No templates); too_many 400 (Over 1000 templates); invalid_template 400 (Nothing usable); bad_body 400 (Body is not a JSON object); read_only_key 403.
curl -X POST "https://app.utmcap.com/api/v1/network-templates" \
-H "Authorization: Bearer $UTMCAP_KEY" \
-H "Content-Type: application/json" \
-d '{ … }'
#DELETE /network-templates
Delete a network template.
| Parameter | Type | Required | Notes |
|---|---|---|---|
id |
uuid | yes |
Answer — data has id, deleted.
Errors besides the usual: invalid_id 400 (id is not a UUID); not_found 404 (Not on this account); read_only_key 403.
curl -X DELETE "https://app.utmcap.com/api/v1/network-templates" \
-H "Authorization: Bearer $UTMCAP_KEY"
#Smartlinks
Short links that route by rule.
#GET /short-links
List smartlinks.
| Parameter | Type | Required | Notes |
|---|---|---|---|
limit |
integer | 1–500, default 500. | |
cursor |
string | next_cursor from the previous page. |
Answer — data has rows, next_cursor.
curl "https://app.utmcap.com/api/v1/short-links" \
-H "Authorization: Bearer $UTMCAP_KEY"
#POST /short-links
Create a smartlink.
Body
| Field | Type | Required | Notes |
|---|---|---|---|
slug |
string | yes | 1–64 characters: letters, digits, _ and -. |
destination_url |
string | yes | Where a visitor goes when no rule matches. http(s). |
domain_id |
uuid | yes | A tracking domain on this account. |
source_id |
uuid | A traffic source on this account. | |
rules |
object[] | Up to 20, checked in order. | |
name |
string | ||
notes |
string | ||
is_active |
boolean | Default true. |
Answer (201) — data has id, slug, name, notes, destination_url, domain_id, source_id, source_name, rules, rule_count, domain, url, is_active, status, clicks, last_click_at, created_at, updated_at.
Errors besides the usual: bad_body 400 (Body is not a JSON object); required 400 (A required field is missing); too_long 400 (A field is too long); invalid_url 400 (A URL is not http(s)); invalid_number 400 (A number field is not a number); out_of_range 400 (A number is outside its range); duplicate_name 409 (The name is in use); invalid_slug 400 (slug has the wrong shape); invalid_domain 400 (domain_id is not a domain on this account); invalid_source 400 (source_id is not a source on this account); invalid_rule_url 400 (A rule destination is not http(s)); invalid_condition 400 (A condition has an unknown attribute or operator); no_conditions 400 (A rule has no conditions); duplicate_slug 400 (The slug is in use); read_only_key 403.
curl -X POST "https://app.utmcap.com/api/v1/short-links" \
-H "Authorization: Bearer $UTMCAP_KEY" \
-H "Content-Type: application/json" \
-d '{ … }'
#PUT /short-links
Replace a smartlink.
Body
| Field | Type | Required | Notes |
|---|---|---|---|
id |
uuid | yes | |
slug |
string | yes | 1–64 characters: letters, digits, _ and -. |
destination_url |
string | yes | Where a visitor goes when no rule matches. http(s). |
domain_id |
uuid | yes | A tracking domain on this account. |
source_id |
uuid | A traffic source on this account. | |
rules |
object[] | Up to 20, checked in order. | |
name |
string | ||
notes |
string | ||
is_active |
boolean | Default true. |
Answer — data has id, slug, name, notes, destination_url, domain_id, source_id, source_name, rules, rule_count, domain, url, is_active, status, clicks, last_click_at, created_at, updated_at.
Errors besides the usual: bad_body 400 (Body is not a JSON object); required 400 (A required field is missing); too_long 400 (A field is too long); invalid_url 400 (A URL is not http(s)); invalid_number 400 (A number field is not a number); out_of_range 400 (A number is outside its range); duplicate_name 409 (The name is in use); invalid_slug 400 (slug has the wrong shape); invalid_domain 400 (domain_id is not a domain on this account); invalid_source 400 (source_id is not a source on this account); invalid_rule_url 400 (A rule destination is not http(s)); invalid_condition 400 (A condition has an unknown attribute or operator); no_conditions 400 (A rule has no conditions); duplicate_slug 400 (The slug is in use); invalid_id 400 (id is not a UUID); not_found 404 (Not on this account); read_only_key 403.
curl -X PUT "https://app.utmcap.com/api/v1/short-links" \
-H "Authorization: Bearer $UTMCAP_KEY" \
-H "Content-Type: application/json" \
-d '{ … }'
#PATCH /short-links
Pause or resume a smartlink.
Body
| Field | Type | Required | Notes |
|---|---|---|---|
id |
uuid | yes | |
is_active |
boolean | yes |
Answer — data has id.
Errors besides the usual: invalid_id 400 (id is not a UUID); not_found 404 (Not on this account); invalid_status 400 (is_active is not true or false); read_only_key 403.
curl -X PATCH "https://app.utmcap.com/api/v1/short-links" \
-H "Authorization: Bearer $UTMCAP_KEY" \
-H "Content-Type: application/json" \
-d '{ … }'
#DELETE /short-links
Delete a smartlink.
| Parameter | Type | Required | Notes |
|---|---|---|---|
id |
uuid | yes |
Answer — data has id, deleted.
Errors besides the usual: invalid_id 400 (id is not a UUID); not_found 404 (Not on this account); read_only_key 403.
curl -X DELETE "https://app.utmcap.com/api/v1/short-links" \
-H "Authorization: Bearer $UTMCAP_KEY"
#POST /short-links/{id}/clone
Copy a smartlink.
The copy is paused and gets a new slug.
Answer (201) — data has The copy; rules is the number of rules copied..
Errors besides the usual: invalid_id 400 (id is not a UUID); not_found 404 (Not on this account); read_only_key 403.
curl -X POST "https://app.utmcap.com/api/v1/short-links/YOUR_ID/clone" \
-H "Authorization: Bearer $UTMCAP_KEY"
#GET /short-links/{id}/stats
A smartlink’s clicks by rule and destination.
| Parameter | Type | Required | Notes |
|---|---|---|---|
from |
string | Start of the window, UTC. Default 6 days ago. | |
to |
string | End of the window, UTC. Default today. |
Answer — data has link { id, name, slug }, range, and the breakdowns..
Errors besides the usual: not_found 404 (Not on this account).
curl "https://app.utmcap.com/api/v1/short-links/YOUR_ID/stats" \
-H "Authorization: Bearer $UTMCAP_KEY"
#DELETE /short-links/{id}/stats
Clear a smartlink’s click history in the window.
Irreversible. The link itself stays.
| Parameter | Type | Required | Notes |
|---|---|---|---|
from |
string | Start of the window, UTC. Default 6 days ago. | |
to |
string | End of the window, UTC. Default today. |
Answer — data has id, range, clicks.
Errors besides the usual: not_found 404 (Not on this account); read_only_key 403.
curl -X DELETE "https://app.utmcap.com/api/v1/short-links/YOUR_ID/stats" \
-H "Authorization: Bearer $UTMCAP_KEY"
#Reports
Figures grouped, over time, and as files.
#GET /reports/performance
Figures grouped by up to five dimensions.
Filter with f.
| Parameter | Type | Required | Notes |
|---|---|---|---|
from |
string | Start of the window, UTC. Default 6 days ago. | |
to |
string | End of the window, UTC. Default today. | |
dimension |
string | Comma-separated, up to 5. Default campaign_id. One of: campaign_id, stream_id, lander_id, offer_id, source_id, lp_slot, country, region, city, language, isp, asn, connection_type, mobile_operator, device_type, device_brand, os, os_version, browser, browser_version, ip, referrer, arrival, bot_reason, proxy_kind, sub1, sub2, sub3, sub4, sub5, sub6, sub7, sub8, sub9, sub10, date, hour, hour_of_day, day_of_week, month. |
Answer — data has range, dimension, dimensions, filters, rows, labels.
Errors besides the usual: invalid_dimension 400 (Unknown dimension, a filter too long, or too many filters).
curl "https://app.utmcap.com/api/v1/reports/performance" \
-H "Authorization: Bearer $UTMCAP_KEY"
#GET /reports/trends
Figures over time.
| Parameter | Type | Required | Notes |
|---|---|---|---|
from |
string | Start of the window, UTC. Default 6 days ago. | |
to |
string | End of the window, UTC. Default today. | |
bucket |
month, day, weekday, hour, weekday_hour |
Default day. | |
tz |
string | An IANA time zone for the buckets, e.g. Asia/Kolkata. Default UTC. |
Answer — data has range, bucket, rows.
Errors besides the usual: invalid_bucket 400 (Unknown bucket).
curl "https://app.utmcap.com/api/v1/reports/trends" \
-H "Authorization: Bearer $UTMCAP_KEY"
#GET /reports/tag-manager
What the tracking tag recorded, broken down.
Goals, pages, campaigns, days or products, with the same figures on each. Rates divide by people rather than by events: one visitor refreshing a confirmation page is one person and two events. On the goal breakdown sent and views are null — clicks are sent to a page, not to a goal — and accepted counts a visitor on every goal they fired, so that column does not add up down the page. The product breakdown comes from the shop’s order lines and so exists only for a connected shop or a server that signs its calls; it adds category, units, orders, unit_price, campaigns and first_at, and leaves accepted at zero. The totals object carries only the columns that add up.
| Parameter | Type | Required | Notes |
|---|---|---|---|
from |
string | Start of the window, UTC. Default 6 days ago. | |
to |
string | End of the window, UTC. Default today. | |
by |
goal, lander, campaign, day, product, trigger, pixel |
Default goal. trigger and pixel say what caused a conversion and what it told: a goal fired by three different buttons is one row on the goal breakdown and three here, which is where a button that stopped working becomes visible. A pixel row counts times the tag CALLED it — whether the network received it is on the far side of their script and cannot be seen from a browser — and only pixels attached to a goal are counted, because one that fires on every page view has no conversion to be counted against. | |
site |
uuid | One measurement site. Default every site. |
Answer — data has by, from, to, summary, rows, totals.
curl "https://app.utmcap.com/api/v1/reports/tag-manager" \
-H "Authorization: Bearer $UTMCAP_KEY"
#GET /reports/lead-campaigns
Lead campaigns, with the pages they sent traffic to.
Live campaigns in lead mode, each with its landing pages under it. Leads count distinct people, not rows — a lead is the submission plus the network’s postback about it, and counting rows doubles it. Cost comes from the reconciled spend, and the cost per lead a client should compute is cost over accepted, not over submissions: a source sending 400 the network rejects is worth less than one sending 150 it pays for. A page with sent above zero and viewed at zero has no tag on it and every lead there is lost; untagged on the campaign counts those pages. Pages carry no cost of their own, because traffic is bought per campaign.
| Parameter | Type | Required | Notes |
|---|---|---|---|
from |
string | Start of the window, UTC. Default 6 days ago. | |
to |
string | End of the window, UTC. Default today. | |
campaign |
uuid | One lead campaign, or every one of them when absent. Narrowed here rather than in the browser, so the totals describe the same set as the rows. |
Answer — data has from, to, rows, totals.
curl "https://app.utmcap.com/api/v1/reports/lead-campaigns" \
-H "Authorization: Bearer $UTMCAP_KEY"
#GET /reports/values
The values a dimension takes, by clicks.
| Parameter | Type | Required | Notes |
|---|---|---|---|
from |
string | Start of the window, UTC. Default 6 days ago. | |
to |
string | End of the window, UTC. Default today. | |
dimension |
string | yes | One of the performance dimensions. |
limit |
integer | 1–1000, default 200. |
Answer — data has dimension, values, labels.
Errors besides the usual: invalid_dimension 400 (dimension is missing or unknown).
curl "https://app.utmcap.com/api/v1/reports/values" \
-H "Authorization: Bearer $UTMCAP_KEY"
#GET /names
What ids were called, including deleted ones.
A log records ids and is read months later, so it needs names for things that no longer exist. The list endpoints leave those out, because they answer what you can choose. Give one or more kinds, comma separated: campaigns, offers, landers, traffic_sources, flows. Each row says whether it is live, in the bin, or removed altogether.
| Parameter | Type | Required | Notes |
|---|---|---|---|
kind |
string | yes | One or more kinds, comma separated. |
Answer — data has rows.
Errors besides the usual: invalid_kind 400 (kind is missing, or names a kind that has no bin).
curl "https://app.utmcap.com/api/v1/names" \
-H "Authorization: Bearer $UTMCAP_KEY"
#GET /reports/exports
Saved report files.
| Parameter | Type | Required | Notes |
|---|---|---|---|
limit |
integer | 1–500, default 500. | |
cursor |
string | next_cursor from the previous page. |
Answer — data has rows, next_cursor.
curl "https://app.utmcap.com/api/v1/reports/exports" \
-H "Authorization: Bearer $UTMCAP_KEY"
#POST /reports/exports
Make a report file (CSV).
The window is from/to in the query string. A read-only key may do this: it changes nothing on the account.
| Parameter | Type | Required | Notes |
|---|---|---|---|
from |
string | Start of the window, UTC. Default 6 days ago. | |
to |
string | End of the window, UTC. Default today. |
Body
| Field | Type | Required | Notes |
|---|---|---|---|
kind |
performance, trends, clicks, conversions, smartlink-clicks, campaigns, repeat-ips |
Default performance. | |
dimension |
string | performance only. Default campaign_id. | |
filters |
object[] | performance only. | |
bucket |
month, day, weekday, hour, weekday_hour |
trends only. Default day. | |
link |
uuid | smartlink-clicks only; all smartlinks when left out. | |
ids |
uuid[] | campaigns only: just these, or all of them when left out. | |
campaign |
uuid | clicks and repeat-ips only. | |
view |
string | clicks only: bots, humans, paid, organic, duplicates or suspect. paid and organic split on whether the source sent a click id. | |
converted |
boolean | clicks only. | |
drills |
object[] | clicks only: the narrowing chips on screen. | |
min_hits |
integer | repeat-ips only. Default 2. | |
columns |
string[] | Column keys, in the order to write them. Everything when left out. | |
name |
string | Up to 255 characters. |
Answer (201) — data has id, name, row_count, byte_size, created_at, download_url.
Errors besides the usual: bad_body 400 (Body is not a JSON object); invalid_kind 400 (Unknown kind); invalid_dimension 400 (Unknown dimension); invalid_bucket 400 (Unknown bucket); invalid_id 400 (link is not a UUID); not_found 404 (No such smartlink); too_large 400 (Over 100,000 rows or 24 MB; use a shorter window).
curl -X POST "https://app.utmcap.com/api/v1/reports/exports" \
-H "Authorization: Bearer $UTMCAP_KEY" \
-H "Content-Type: application/json" \
-d '{ … }'
#DELETE /reports/exports
Delete a report file.
| Parameter | Type | Required | Notes |
|---|---|---|---|
id |
uuid | yes |
Answer — data has id, deleted.
Errors besides the usual: invalid_id 400 (id is not a UUID); not_found 404 (No such file); read_only_key 403.
curl -X DELETE "https://app.utmcap.com/api/v1/reports/exports" \
-H "Authorization: Bearer $UTMCAP_KEY"
#GET /reports/exports/{id}
Download a report file.
Answers text/csv (UTF-8 with a byte-order mark) as an attachment, not JSON. Errors are JSON.
Answer — data has A JSON object..
Errors besides the usual: invalid_id 400 (id is not a UUID); not_found 404 (No such file).
curl "https://app.utmcap.com/api/v1/reports/exports/YOUR_ID" \
-H "Authorization: Bearer $UTMCAP_KEY"
#GET /reports/orphaned
Clicks recorded for campaigns that no longer exist.
| Parameter | Type | Required | Notes |
|---|---|---|---|
from |
string | Start of the window, UTC. Default 6 days ago. | |
to |
string | End of the window, UTC. Default today. | |
totals_for |
string | Comma-separated campaign ids: answer all-time totals for just these. |
Answer — data has range, rows[{ campaign_id, clicks, bot_clicks, lp_clicks, visitors, cost, conversions, revenue, first_seen, last_seen }], liveCampaigns — or, with totals_for, { clicks, cost, conversions, revenue }..
Errors besides the usual: invalid_id 400 (A campaign id is not a UUID).
curl "https://app.utmcap.com/api/v1/reports/orphaned" \
-H "Authorization: Bearer $UTMCAP_KEY"
#DELETE /reports/orphaned
Delete clicks and conversions of campaigns that no longer exist.
Irreversible. Refused for a campaign that still exists.
Body
| Field | Type | Required | Notes |
|---|---|---|---|
campaign_ids |
uuid[] | yes | 1–100. |
Answer — data has deleted, clicks.
Errors besides the usual: invalid_request 400 (campaign_ids missing or not UUIDs); too_many 400 (Over 100); not_orphaned 409 (A campaign still exists); read_only_key 403.
curl -X DELETE "https://app.utmcap.com/api/v1/reports/orphaned" \
-H "Authorization: Bearer $UTMCAP_KEY" \
-H "Content-Type: application/json" \
-d '{ … }'
#Stats
The dashboard’s headline numbers.
#GET /stats/overview
Totals for the window, and the change from the one before.
| Parameter | Type | Required | Notes |
|---|---|---|---|
from |
string | Start of the window, UTC. Default 6 days ago. | |
to |
string | End of the window, UTC. Default today. |
Answer — data has range, previousRange, clicks, unique_clicks, lp_clicks, bot_clicks, conversions, pending, rejected, cost, revenue, profit, roi, cr, lp_ctr, epc, cpa, changes.
curl "https://app.utmcap.com/api/v1/stats/overview" \
-H "Authorization: Bearer $UTMCAP_KEY"
#GET /stats/timeseries
Clicks, conversions, cost and revenue by day.
| Parameter | Type | Required | Notes |
|---|---|---|---|
from |
string | Start of the window, UTC. Default 6 days ago. | |
to |
string | End of the window, UTC. Default today. |
Answer — data has range, points.
curl "https://app.utmcap.com/api/v1/stats/timeseries" \
-H "Authorization: Bearer $UTMCAP_KEY"
#GET /stats/hourly
Today by hour (UTC).
Answer — data has points.
curl "https://app.utmcap.com/api/v1/stats/hourly" \
-H "Authorization: Bearer $UTMCAP_KEY"
#GET /stats/breakdown
Top 20 traffic sources by clicks.
| Parameter | Type | Required | Notes |
|---|---|---|---|
from |
string | Start of the window, UTC. Default 6 days ago. | |
to |
string | End of the window, UTC. Default today. |
Answer — data has range, rows.
curl "https://app.utmcap.com/api/v1/stats/breakdown" \
-H "Authorization: Bearer $UTMCAP_KEY"
#GET /stats/live
Who is on your pages right now.
| Parameter | Type | Required | Notes |
|---|---|---|---|
minutes |
integer | 1–60, default 5. |
Answer — data has visitors, views, windowMinutes, attributed, visitorsBefore, countryCount, pages, countries, devices, browsers.
curl "https://app.utmcap.com/api/v1/stats/live" \
-H "Authorization: Bearer $UTMCAP_KEY"
#Logs
Individual clicks and conversions.
#GET /logs/clicks
Individual clicks, newest first.
from is held to the start of the plan’s click history.
| Parameter | Type | Required | Notes |
|---|---|---|---|
from |
string | Start of the window, UTC. Default 6 days ago. | |
to |
string | End of the window, UTC. Default today. | |
limit |
integer | 1–1000, default 100. | |
offset |
integer | Rows to skip, default 0. | |
converted |
1 |
Only clicks that converted. |
Answer — data has range, rows.
curl "https://app.utmcap.com/api/v1/logs/clicks" \
-H "Authorization: Bearer $UTMCAP_KEY"
#GET /logs/clicks/values
The values one clicks-log column takes, with a count each.
What the log’s “narrow” picker offers. Takes the same narrowing as the log itself — campaign, view, converted, and d.
| Parameter | Type | Required | Notes |
|---|---|---|---|
from |
string | Start of the window, UTC. Default 6 days ago. | |
to |
string | End of the window, UTC. Default today. | |
facet |
string | yes | A clicks-log column: country, os, isp, sub1 … |
campaign |
uuid | ||
view |
bots, humans, paid, organic, duplicates, suspect |
||
converted |
1 |
Only clicks that converted. |
Answer — data has facet, rows.
Errors besides the usual: invalid_facet 400 (No such column).
curl "https://app.utmcap.com/api/v1/logs/clicks/values" \
-H "Authorization: Bearer $UTMCAP_KEY"
#GET /logs/clicks/{clickId}
One click: its hops, conversions and visitor.
Answer — data has click_id, hops, conversions, names, visitor.
Errors besides the usual: invalid_id 400 (Not a click id); not_found 404 (Click not found); plan_limit 402 (The click is older than the plan’s click history).
curl "https://app.utmcap.com/api/v1/logs/clicks/YOUR_CLICKID" \
-H "Authorization: Bearer $UTMCAP_KEY"
#GET /logs/conversions
Individual conversions, newest first.
| Parameter | Type | Required | Notes |
|---|---|---|---|
from |
string | Start of the window, UTC. Default 6 days ago. | |
to |
string | End of the window, UTC. Default today. | |
limit |
integer | 1–1000, default 100. | |
offset |
integer | Rows to skip, default 0. |
Answer — data has range, rows.
curl "https://app.utmcap.com/api/v1/logs/conversions" \
-H "Authorization: Bearer $UTMCAP_KEY"
#GET /logs/repeat-ips
IP addresses that clicked more than once.
| Parameter | Type | Required | Notes |
|---|---|---|---|
from |
string | Start of the window, UTC. Default 6 days ago. | |
to |
string | End of the window, UTC. Default today. | |
min_hits |
integer | 2–100000, default 2. | |
limit |
integer | 1–2000, default 500. | |
campaign |
string | A campaign id, or all. | |
source |
string | A traffic source id, or all. | |
link |
string | A smartlink id, or links for every smartlink. Switches to smartlink clicks. |
Answer — data has range, min_hits, of, rows.
Errors besides the usual: invalid_id 400 (campaign, source or link is not a UUID); not_found 404 (No such smartlink).
curl "https://app.utmcap.com/api/v1/logs/repeat-ips" \
-H "Authorization: Bearer $UTMCAP_KEY"
#GET /logs/smartlink-clicks
Individual smartlink clicks, newest first.
| Parameter | Type | Required | Notes |
|---|---|---|---|
from |
string | Start of the window, UTC. Default 6 days ago. | |
to |
string | End of the window, UTC. Default today. | |
limit |
integer | 1–1000, default 100. | |
offset |
integer | Rows to skip, default 0. | |
link |
string | A smartlink id, or all. |
Answer — data has range, rows.
Errors besides the usual: invalid_id 400 (link is not a UUID); not_found 404 (No such smartlink).
curl "https://app.utmcap.com/api/v1/logs/smartlink-clicks" \
-H "Authorization: Bearer $UTMCAP_KEY"
#Corrections
Fixing conversions and spend in bulk.
#POST /corrections/conversions
Record or revise conversions from a CSV.
Columns (header names are matched loosely): click id (required), payout, status (pending, approved, rejected, chargeback), currency, conversion id, time, goal. A dry run unless apply is true.
Body
| Field | Type | Required | Notes |
|---|---|---|---|
csv |
string | yes | CSV text with a header row. Up to 5000 rows are read. |
apply |
boolean | Only true writes; anything else is a dry run. |
Answer — data has dryRun, total, accepted, refused, payout, truncated, outcomes.
Errors besides the usual: empty 400 (Nothing to import); no_rows 400 (No rows under the header); read_only_key 403.
curl -X POST "https://app.utmcap.com/api/v1/corrections/conversions" \
-H "Authorization: Bearer $UTMCAP_KEY" \
-H "Content-Type: application/json" \
-d '{ … }'
#POST /corrections/cost
Set spend by day and source from a CSV.
Columns: day (YYYY-MM-DD), source (id or name), spend (required); campaign, currency, clicks, impressions. A dry run unless apply is true.
Body
| Field | Type | Required | Notes |
|---|---|---|---|
csv |
string | yes | CSV text with a header row. Up to 5000 rows are read. |
apply |
boolean | Only true writes; anything else is a dry run. |
Answer — data has dryRun, total, accepted, refused, spend, truncated, outcomes.
Errors besides the usual: empty 400 (Nothing to import); no_rows 400 (No rows under the header); read_only_key 403.
curl -X POST "https://app.utmcap.com/api/v1/corrections/cost" \
-H "Authorization: Bearer $UTMCAP_KEY" \
-H "Content-Type: application/json" \
-d '{ … }'
#Bot protection
Bot scoring and allow and deny lists.
#GET /bot-lists
Allow and deny lists, and what was blocked.
| Parameter | Type | Required | Notes |
|---|---|---|---|
from |
string | Start of the window, UTC. Default 6 days ago. | |
to |
string | End of the window, UTC. Default today. | |
limit |
integer | 1–500, default 500. | |
cursor |
string | next_cursor from the previous page. |
Answer — data has range, propagationSeconds, blocked, rows, next_cursor.
curl "https://app.utmcap.com/api/v1/bot-lists" \
-H "Authorization: Bearer $UTMCAP_KEY"
#POST /bot-lists
Add an allow or deny entry.
Body
| Field | Type | Required | Notes |
|---|---|---|---|
list_type |
allow, deny |
yes | |
attribute |
string | yes | ip, ip_range, asn, country, isp, user_agent, referrer, or sub1…sub10. |
operator |
equals, contains, regex, in_cidr |
Default equals. | |
value |
any | yes | A value, or an array of values. |
campaign_id |
uuid | Only this campaign; all campaigns when left out. | |
note |
string |
Answer (201) — data has id, list_type, attribute, operator, value, note.
Errors besides the usual: bad_body 400 (Body is not a JSON object); invalid_list_type 400 (Unknown list_type); invalid_attribute 400 (Unknown attribute); invalid_operator 400 (Unknown operator); value_required 400 (No value); invalid_regex 400 (A regex value does not compile); invalid_campaign 400 (campaign_id is not a campaign on this account); read_only_key 403.
curl -X POST "https://app.utmcap.com/api/v1/bot-lists" \
-H "Authorization: Bearer $UTMCAP_KEY" \
-H "Content-Type: application/json" \
-d '{ … }'
#DELETE /bot-lists
Remove an allow or deny entry.
| Parameter | Type | Required | Notes |
|---|---|---|---|
id |
uuid | yes |
Answer — data has id, deleted.
Errors besides the usual: invalid_id 400 (id is not a UUID); not_found 404 (No such entry); read_only_key 403.
curl -X DELETE "https://app.utmcap.com/api/v1/bot-lists" \
-H "Authorization: Bearer $UTMCAP_KEY"
#GET /bot-detection
Bot scoring settings and the last 30 days.
Each signal’s clicks counts the flagged clicks of the last 30 days that carried it. A bot that carried three signals counts under all three, so the signal counts overlap and do not add up to totals.bots. fired, humans and buyers count every click the signal fired on, whether it was counted or only watched, and overlap the same way. Each signal has a mode: on counts it in the score, watch records it on the click without counting it, off ignores it.
Answer — data has threshold, minThreshold, maxThreshold, signals, totals, unknownReasons, lists.
curl "https://app.utmcap.com/api/v1/bot-detection" \
-H "Authorization: Bearer $UTMCAP_KEY"
#PUT /bot-detection
Change the bot threshold and each signal’s mode.
A field left out keeps its value. A signal is On unless it is in disabled (Off) or watched (Watch). Sending only disabled keeps every other signal’s Watch as it was; sending only watched moves those signals out of Off. Signals that cannot be switched off are always On and are ignored in both lists.
Body
| Field | Type | Required | Notes |
|---|---|---|---|
threshold |
number | Rounded and held to 20–95. | |
disabled |
string[] | Signal reasons to switch off; ones that cannot be are ignored. Up to 40. | |
watched |
string[] | Signal reasons to watch: detected and recorded, never counted. Ones that cannot be switched off, and unknown reasons, are ignored. Up to 40. |
Answer — data has threshold, disabled, watched.
Errors besides the usual: invalid_watched 400 (watched is not a list); signal_in_two_lists 400 (A signal is in both disabled and watched); read_only_key 403.
curl -X PUT "https://app.utmcap.com/api/v1/bot-detection" \
-H "Authorization: Bearer $UTMCAP_KEY" \
-H "Content-Type: application/json" \
-d '{ … }'
#Domains
Tracking domains, DNS and certificates.
#GET /domains
Tracking domains, and the shared link domains you can use.
| Parameter | Type | Required | Notes |
|---|---|---|---|
limit |
integer | 1–500, default 500. | |
cursor |
string | next_cursor from the previous page. |
Answer — data has rows, next_cursor, shared, cname_target.
curl "https://app.utmcap.com/api/v1/domains" \
-H "Authorization: Bearer $UTMCAP_KEY"
#POST /domains
Add one or more tracking domains.
domain may hold several hostnames separated by commas or spaces (up to 50; 10 with dns_account_id).
Body
| Field | Type | Required | Notes |
|---|---|---|---|
domain |
string | yes | Hostnames; http(s)://, paths and ports are stripped. |
dns_account_id |
uuid | A connected DNS account: point the domains at the tracker automatically. | |
group_name |
string | Up to 100 characters. | |
crawlers |
allow, disallow |
Default disallow. | |
https_only |
boolean | Default true. | |
index_page |
string | Where the bare domain sends a visitor. http(s), or empty. |
Answer (201) — data has id, domain, is_verified, added, skipped, dns.
Errors besides the usual: plan_limit 402 (The plan domains limit is reached); required 400 (domain is missing); too_long 400 (domain is over 4000 characters); invalid_domain 400 (Not a hostname); too_many 400 (Too many hostnames at once); reserved_domain 400 (A hostname this install keeps for itself); shared_domain 409 (A shared link domain); invalid_dns_account 400 (dns_account_id is not a UUID); dns_account_not_found 400 (No such connected DNS account); invalid_index_page 400 (index_page is not http(s)); duplicate 409 (Every hostname is already added); bad_body 400 (Body is not a JSON object); read_only_key 403.
curl -X POST "https://app.utmcap.com/api/v1/domains" \
-H "Authorization: Bearer $UTMCAP_KEY" \
-H "Content-Type: application/json" \
-d '{ … }'
#PATCH /domains
Change a domain’s settings, or make it the default.
With action set_default only the default changes. Otherwise the four settings are replaced: one left out goes back to its default.
Body
| Field | Type | Required | Notes |
|---|---|---|---|
id |
uuid | yes | |
action |
set_default |
||
group_name |
string | Up to 100 characters. | |
crawlers |
allow, disallow |
Default disallow. | |
https_only |
boolean | Default true. | |
index_page |
string | Where the bare domain sends a visitor. http(s), or empty. |
Answer — data has { id, domain } or, for set_default, { id, domain, is_default }..
Errors besides the usual: invalid_id 400 (id is not a UUID); invalid_index_page 400 (index_page is not http(s)); not_found 404 (Not on this account); bad_body 400 (Body is not a JSON object); read_only_key 403.
curl -X PATCH "https://app.utmcap.com/api/v1/domains" \
-H "Authorization: Bearer $UTMCAP_KEY" \
-H "Content-Type: application/json" \
-d '{ … }'
#DELETE /domains
Remove a tracking domain.
| Parameter | Type | Required | Notes |
|---|---|---|---|
id |
uuid | yes |
Answer — data has id, deleted, certificate_released.
Errors besides the usual: invalid_id 400 (id is not a UUID); domain_in_use 409 (Campaigns still use it); not_found 404 (Not on this account); read_only_key 403.
curl -X DELETE "https://app.utmcap.com/api/v1/domains" \
-H "Authorization: Bearer $UTMCAP_KEY"
#POST /domains/{id}/certificate
Request the domain’s HTTPS certificate at the edge.
Answer (201) — data has hostname, ssl_status, serving, detail, ownership, ownership_http, errors..
Errors besides the usual: invalid_id 400 (id is not a UUID); not_found 404 (Not on this account); cloudflare_not_configured 503 (This install has no Cloudflare connection); cloudflare_error 502 (Cloudflare refused; the code is cloudflare_read_only_key 403.
curl -X POST "https://app.utmcap.com/api/v1/domains/YOUR_ID/certificate" \
-H "Authorization: Bearer $UTMCAP_KEY"
#GET /domains/{id}/certificate
The certificate’s state.
Answer — data has hostname, ssl_status, serving, detail, ownership, ownership_http, errors..
Errors besides the usual: invalid_id 400 (id is not a UUID); not_found 404 (Not on this account); cloudflare_not_configured 503 (This install has no Cloudflare connection); cloudflare_error 502 (Cloudflare refused; the code is cloudflare_
curl "https://app.utmcap.com/api/v1/domains/YOUR_ID/certificate" \
-H "Authorization: Bearer $UTMCAP_KEY"
#DELETE /domains/{id}/certificate
Remove the domain’s certificate.
Answer — data has deleted, detail.
Errors besides the usual: invalid_id 400 (id is not a UUID); not_found 404 (Not on this account); cloudflare_not_configured 503 (This install has no Cloudflare connection); cloudflare_error 502 (Cloudflare refused; the code is cloudflare_read_only_key 403.
curl -X DELETE "https://app.utmcap.com/api/v1/domains/YOUR_ID/certificate" \
-H "Authorization: Bearer $UTMCAP_KEY"
#POST /domains/{id}/point
Point the domain at the tracker through a connected DNS account.
Answer — data has ok, provider, message, detail, certificate, note.
Errors besides the usual: not_found 404 (Not on this account); no_provider 400 (No connected DNS account holds this domain); read_only_key 403.
curl -X POST "https://app.utmcap.com/api/v1/domains/YOUR_ID/point" \
-H "Authorization: Bearer $UTMCAP_KEY"
#POST /domains/{id}/verify
Check the domain points at the tracker and serves HTTPS.
Answer — data has id, domain, expected, verified, points, detail, https, records, checked_at.
Errors besides the usual: invalid_id 400 (id is not a UUID); not_found 404 (Not on this account); tracking_domain_unset 503 (This install has no tracking hostname set); read_only_key 403.
curl -X POST "https://app.utmcap.com/api/v1/domains/YOUR_ID/verify" \
-H "Authorization: Bearer $UTMCAP_KEY"
#Goals
Conversion goals on your own sites.
#GET /goals
A site’s conversion goals, with their figures.
| Parameter | Type | Required | Notes |
|---|---|---|---|
site |
uuid | A site id. Default: the first active site. |
Answer — data has site, sites, goals, siteKey, secretSet, secretSetAt, currency, products, live, siteTotals, suggested, planIncludes, trackingDomains, sharedDomains.
curl "https://app.utmcap.com/api/v1/goals" \
-H "Authorization: Bearer $UTMCAP_KEY"
#GET /goals/triggers
What makes a site’s goals fire from the page.
A trigger belongs to a goal and fires that goal — never a bare conversion, because the goal carries the value and the dedupe rule. match_on is a list of candidates, most specific first, and the tag takes the first that resolves: a single selector dies silently the day somebody renames a class, and the fallbacks keep the goal firing until it is fixed properly.
| Parameter | Type | Required | Notes |
|---|---|---|---|
site |
uuid | yes |
Answer — data has triggers.
Errors besides the usual: no_site 400 (site is missing).
curl "https://app.utmcap.com/api/v1/goals/triggers" \
-H "Authorization: Bearer $UTMCAP_KEY"
#POST /goals/triggers
Add a trigger to a goal.
The goal must be on the site given, and must belong to this account. A css candidate that would match the whole page is refused: every click anywhere would be recorded as a conversion, and conversions cannot be un-recorded.
Body
| Field | Type | Required | Notes |
|---|---|---|---|
goalId |
uuid | yes | |
siteId |
uuid | yes | |
kind |
click, submit, view, timer, leave, exit |
yes | |
name |
string | Up to 120 characters, for the list. Not used for matching. | |
matchOn |
object[] | Up to 6, most specific first. Not used by timer, leave or exit. | |
pageMatch |
string | Path prefix. Default: every page of the site. | |
seconds |
integer | timer: 1 to 3600. leave: 0 to 3600, where 0 counts every visitor. |
Answer — data has trigger.
Errors besides the usual: invalid_kind 400 (kind is not click, submit, view, timer, leave or exit); invalid_trigger 400 (The message says what to change: too broad a selector, a malformed one, a timer out of range, or a goal that is not on that site); read_only_key 403.
curl -X POST "https://app.utmcap.com/api/v1/goals/triggers" \
-H "Authorization: Bearer $UTMCAP_KEY" \
-H "Content-Type: application/json" \
-d '{ … }'
#PATCH /goals/triggers
Change a trigger.
Editing clears publishedAt, because the edge is still serving the previous version until the next publish and a row claiming otherwise would have the screen and the page disagreeing.
Body
| Field | Type | Required | Notes |
|---|---|---|---|
id |
uuid | yes | |
name |
string | ||
matchOn |
object[] | ||
pageMatch |
string | ||
seconds |
integer | ||
isActive |
boolean |
Answer — data has trigger.
Errors besides the usual: no_id 400 (id is missing); not_found 404 (No such trigger); invalid_trigger 400 (See POST); read_only_key 403.
curl -X PATCH "https://app.utmcap.com/api/v1/goals/triggers" \
-H "Authorization: Bearer $UTMCAP_KEY" \
-H "Content-Type: application/json" \
-d '{ … }'
#DELETE /goals/triggers
Remove a trigger.
| Parameter | Type | Required | Notes |
|---|---|---|---|
id |
uuid | yes |
Answer — data has deleted.
Errors besides the usual: no_id 400 (id is missing); not_found 404 (No such trigger); read_only_key 403.
curl -X DELETE "https://app.utmcap.com/api/v1/goals/triggers" \
-H "Authorization: Bearer $UTMCAP_KEY"
#GET /goals/picked
Elements picked on the site, waiting to become triggers.
Opening any page of a site that has the tag with ?utmcap_pick=1 turns it into a picker for one visit: click the element and it appears here. No browser extension, because the tag is already on the page. Nothing is recorded while picking — no page views, no goals. Each candidate says how it was derived and whether it is fragile; one whose only candidate is fragile will break on the next redesign, which is the thing worth warning about. Picks are a clipboard, not a record: they are cleared after a day.
| Parameter | Type | Required | Notes |
|---|---|---|---|
site |
uuid | yes |
Answer — data has picked, watching, watchingUntil.
Errors besides the usual: no_site 400 (site is missing).
curl "https://app.utmcap.com/api/v1/goals/picked" \
-H "Authorization: Bearer $UTMCAP_KEY"
#POST /goals/picked
Watch what visitors press that nothing is recording.
The other way round from every tag manager, which asks you to imagine an element and write a selector for it. For a window you open by hand, the tag reports the controls visitors clicked that no trigger covers, and they appear in GET /goals/picked with a count of how many people pressed each one — so a goal is made from something that happened rather than something predicted. Fenced, because it watches real visitors: off unless asked, at most 120 minutes and 30 by default, expiring on its own so it cannot be left on, capped at a handful of reports per page view, controls only, and never the value of a field or a recording of the screen. {"watch": false} closes it early.
Body
| Field | Type | Required | Notes |
|---|---|---|---|
site |
uuid | yes | |
watch |
boolean | Omitted or true opens a window; false closes it. | |
minutes |
integer | How long, up to 120. Default 30. |
Answer — data has watching, watchingUntil.
Errors besides the usual: no_site 400 (site is missing); not_found 404 (No such site); read_only_key 403.
curl -X POST "https://app.utmcap.com/api/v1/goals/picked" \
-H "Authorization: Bearer $UTMCAP_KEY" \
-H "Content-Type: application/json" \
-d '{ … }'
#GET /goals/publish
What the tag is being served, and what is only drafted.
Triggers and pixels are a draft until published. Before this they were served the moment they were saved, so a half-typed selector was running on a customer’s website while somebody was still deciding on it. pending is true when the draft differs from what the tag is serving — compared as the published shape, not by counting rows, because an edit that changes a selector changes neither count. Pair the times here with a goal that stopped firing: the first question is always whether somebody published just before it went quiet.
| Parameter | Type | Required | Notes |
|---|---|---|---|
site |
uuid | yes |
Answer — data has live, pending, history, canUndo.
Errors besides the usual: no_site 400 (site is missing).
curl "https://app.utmcap.com/api/v1/goals/publish" \
-H "Authorization: Bearer $UTMCAP_KEY"
#POST /goals/publish
Publish the draft, or put the last set back.
{"undo": true} restores the previous snapshot. Both are the same endpoint because they are the same event in the history — an undo is a publish carrying an older set, and somebody asking why conversions moved on Tuesday needs to see the mistake and the correction, not a gap where the mistake used to be. What is stored is exactly the shape the browser reads, so publishing cannot turn the draft into something subtly different from what was being previewed. Pages pick up a new publish within five minutes.
Body
| Field | Type | Required | Notes |
|---|---|---|---|
site |
uuid | yes | |
undo |
boolean | True puts the previous set back. |
Answer — data has published, undone.
Errors besides the usual: no_site 400 (site is missing); not_found 404 (No such site); nothing_to_undo 400 (There is no earlier publish to go back to); read_only_key 403.
curl -X POST "https://app.utmcap.com/api/v1/goals/publish" \
-H "Authorization: Bearer $UTMCAP_KEY" \
-H "Content-Type: application/json" \
-d '{ … }'
#GET /goals/variables
Values the tag reads off the page.
A pixel could carry {{click_id}} and nothing about what happened, so a network was told a purchase occurred and never what it was worth — and one bidding on "a purchase" cannot tell a £9 order from a £900 one. A page value reads the words in an element, or one of its attributes, and fills a {{name}} in a pixel or a link parameter. Two limits are the feature rather than details: it cannot read a form field — whatever somebody types is theirs — and it never changes the conversion UTMCAP records, because a number read off a page is a number any visitor with developer tools can set. Name one revenue and the pixels use it with no further configuration.
| Parameter | Type | Required | Notes |
|---|---|---|---|
site |
uuid | yes |
Answer — data has variables.
Errors besides the usual: no_site 400 (site is missing).
curl "https://app.utmcap.com/api/v1/goals/variables" \
-H "Authorization: Bearer $UTMCAP_KEY"
#POST /goals/variables
Add a page value.
The refusals say what to change. The one worth knowing about before you try: a selector that would match an input, textarea or select is refused outright, because sending a visitor’s own typing to an ad network is not something to make easy.
Body
| Field | Type | Required | Notes |
|---|---|---|---|
siteId |
uuid | yes | |
name |
string | yes | Lowercase letters, numbers and underscores. click_id and goal are taken. |
how |
text, attr |
yes | |
selector |
string | yes | |
attribute |
string | Required when how=attr. | |
clean |
none, number |
Default none. |
Answer — data has variable.
Errors besides the usual: invalid_variable 400 (The message names what to change); read_only_key 403.
curl -X POST "https://app.utmcap.com/api/v1/goals/variables" \
-H "Authorization: Bearer $UTMCAP_KEY" \
-H "Content-Type: application/json" \
-d '{ … }'
#DELETE /goals/variables
Remove a page value.
| Parameter | Type | Required | Notes |
|---|---|---|---|
id |
uuid | yes |
Answer — data has deleted.
Errors besides the usual: no_id 400 (id is missing); not_found 404 (No such value); read_only_key 403.
curl -X DELETE "https://app.utmcap.com/api/v1/goals/variables" \
-H "Authorization: Bearer $UTMCAP_KEY"
#DELETE /goals/picked
Dismiss a pick.
| Parameter | Type | Required | Notes |
|---|---|---|---|
id |
uuid | yes |
Answer — data has deleted.
Errors besides the usual: no_id 400 (id is missing); not_found 404 (No such pick); read_only_key 403.
curl -X DELETE "https://app.utmcap.com/api/v1/goals/picked" \
-H "Authorization: Bearer $UTMCAP_KEY"
#GET /goals/actions
What the tag fires besides recording — network pixels.
The pixel fires and the conversion is still reported server-side. The pixel feeds the network’s optimiser; the server-side record is the one that survives an ad blocker and an expired cookie. Nothing here changes what UTMCAP records: a blocked or mistyped pixel costs the network’s copy, never yours. networks returns the templates the form and the validation share, so a screen cannot offer an event the server refuses.
| Parameter | Type | Required | Notes |
|---|---|---|---|
site |
uuid | yes |
Answer — data has actions, networks.
Errors besides the usual: no_site 400 (site is missing).
curl "https://app.utmcap.com/api/v1/goals/actions" \
-H "Authorization: Bearer $UTMCAP_KEY"
#POST /goals/actions
Add a pixel, or parameters on outbound links.
Ids are checked against each network’s own shape. What that really catches is the wrong id entirely — a Meta ad account id in the pixel box, or a Google conversion id with the label still attached. Both look plausible, fire nothing, and produce no error anywhere. kind=link is a different thing: it writes parameters onto links that leave the site, so a click id reaches whoever the traffic is being sent to. That is the address only — no redirect, nothing recorded, and the call-to-action links the tag already rewrites are left alone. kind=event tells the page itself: the tag dispatches a CustomEvent named eventName on window, with detail.goal and detail.clickId.
Body
| Field | Type | Required | Notes |
|---|---|---|---|
siteId |
uuid | yes | |
kind |
pixel, image, link, event |
yes | |
network |
meta, tiktok, google, snap, reddit |
pixel only. | |
pixelId |
string | yes | The network id, or for an image the https URL. May contain {{click_id}}, {{goal}}, {{revenue}}, {{currency}}. |
eventName |
string | The network’s own event name for a pixel; for kind=event, the name the page listens for, e.g. utmcap:open-demo. | |
extra |
string | Google’s conversion label. | |
firesOn |
string | A goal key. Empty is every page view. Ignored for a link. | |
linkMatch |
string | link only. A hostname, or part of one — no scheme and no path. Empty is every link leaving the site. | |
linkParams |
string | link only. A query string: cid={{click_id}}&src=utmcap. A value is a plain word or one of the variables; a variable with no value drops its parameter rather than sending it empty. |
Answer — data has action.
Errors besides the usual: invalid_kind 400 (kind is not pixel or image); invalid_action 400 (The message says what to change: an id in the wrong shape, a missing Google label, a non-https image URL, or a site that is not yours); read_only_key 403.
curl -X POST "https://app.utmcap.com/api/v1/goals/actions" \
-H "Authorization: Bearer $UTMCAP_KEY" \
-H "Content-Type: application/json" \
-d '{ … }'
#PATCH /goals/actions
Turn a pixel on or off.
Body
| Field | Type | Required | Notes |
|---|---|---|---|
id |
uuid | yes | |
isActive |
boolean |
Answer — data has action.
Errors besides the usual: no_id 400 (id is missing); not_found 404 (No such action); read_only_key 403.
curl -X PATCH "https://app.utmcap.com/api/v1/goals/actions" \
-H "Authorization: Bearer $UTMCAP_KEY" \
-H "Content-Type: application/json" \
-d '{ … }'
#DELETE /goals/actions
Remove a pixel.
| Parameter | Type | Required | Notes |
|---|---|---|---|
id |
uuid | yes |
Answer — data has deleted.
Errors besides the usual: no_id 400 (id is missing); not_found 404 (No such action); read_only_key 403.
curl -X DELETE "https://app.utmcap.com/api/v1/goals/actions" \
-H "Authorization: Bearer $UTMCAP_KEY"
#PUT /goals
Create or change a goal, by key.
Body
| Field | Type | Required | Notes |
|---|---|---|---|
key |
string | yes | Lower case letters, digits and _. |
name |
string | Up to 120 characters. Default: the key. | |
kind |
event, page |
Default event. | |
matchPath |
string | For a page goal: the path that fires it. | |
dedupe |
per_click, per_order |
Default per_click. | |
valueMode |
none, fixed, from_event |
Default none. | |
value |
number | For fixed: above 0. | |
siteId |
uuid | Default: the first active site. | |
currency |
string | Three letters. | |
status |
pending, approved |
Default approved. | |
isPrimary |
boolean | ||
isActive |
boolean |
Answer — data has goal.
Errors besides the usual: bad_key 400 (key has the wrong shape); bad_goal_shape 400 (kind, dedupe and matchPath do not fit together); no_value 400 (fixed without a value above 0); no_site 400 (No site to put it on); duplicate_name 409 (Another goal on the site has this name); plan_limit 402 (The plan has no goal tracking (new goals only)); read_only_key 403.
curl -X PUT "https://app.utmcap.com/api/v1/goals" \
-H "Authorization: Bearer $UTMCAP_KEY" \
-H "Content-Type: application/json" \
-d '{ … }'
#POST /goals
Rotate the server-to-server goal secret.
The new secret is shown once. Servers using the old one stop being believed.
Answer — data has secret, note.
Errors besides the usual: read_only_key 403.
curl -X POST "https://app.utmcap.com/api/v1/goals" \
-H "Authorization: Bearer $UTMCAP_KEY"
#DELETE /goals
Delete a goal.
| Parameter | Type | Required | Notes |
|---|---|---|---|
key |
string | yes | |
site |
uuid |
Answer — data has deleted.
Errors besides the usual: no_key 400 (key is missing); no_site 400 (No such site); read_only_key 403.
curl -X DELETE "https://app.utmcap.com/api/v1/goals" \
-H "Authorization: Bearer $UTMCAP_KEY"
#GET /goals/landers
Lead campaigns and the landing pages their clicks reached.
Answer — data has campaigns.
curl "https://app.utmcap.com/api/v1/goals/landers" \
-H "Authorization: Bearer $UTMCAP_KEY"
#GET /sites
Sites that report goals.
Answer — data has sites.
curl "https://app.utmcap.com/api/v1/sites" \
-H "Authorization: Bearer $UTMCAP_KEY"
#POST /sites
Add a site.
Body
| Field | Type | Required | Notes |
|---|---|---|---|
name |
string | yes | Up to 120 characters. |
domain |
string | The site’s hostname; a URL is reduced to its host. | |
currency |
string | Three letters. Default USD. | |
kind |
store, lead |
Default store. | |
revenueFrom |
order, fixed |
Default order. | |
isActive |
boolean | ||
seedGoals |
boolean | Add the suggested goals too. |
Answer — data has site.
Errors besides the usual: invalid 400 (A field has the wrong shape); plan_limit 402 (The plan has no goal tracking); duplicate_name 409 (A site of this kind has this name); read_only_key 403.
curl -X POST "https://app.utmcap.com/api/v1/sites" \
-H "Authorization: Bearer $UTMCAP_KEY" \
-H "Content-Type: application/json" \
-d '{ … }'
#PUT /sites
Change a site.
Body
| Field | Type | Required | Notes |
|---|---|---|---|
id |
uuid | yes | |
name |
string | yes | Up to 120 characters. |
domain |
string | The site’s hostname; a URL is reduced to its host. | |
currency |
string | Three letters. Default USD. | |
kind |
store, lead |
Default store. | |
revenueFrom |
order, fixed |
Default order. | |
isActive |
boolean |
Answer — data has site.
Errors besides the usual: no_id 400 (id is missing); not_found 404 (Not on this account); invalid 400 (A field has the wrong shape); duplicate_name 409 (A site of this kind has this name); read_only_key 403.
curl -X PUT "https://app.utmcap.com/api/v1/sites" \
-H "Authorization: Bearer $UTMCAP_KEY" \
-H "Content-Type: application/json" \
-d '{ … }'
#DELETE /sites
Delete a site.
| Parameter | Type | Required | Notes |
|---|---|---|---|
id |
uuid | yes |
Answer — data has deleted.
Errors besides the usual: no_id 400 (id is missing); read_only_key 403.
curl -X DELETE "https://app.utmcap.com/api/v1/sites" \
-H "Authorization: Bearer $UTMCAP_KEY"
#POST /sites/lead
The account’s lead site, made if there is none.
Answer — data has site.
Errors besides the usual: plan_limit 402 (The plan has no goal tracking); read_only_key 403.
curl -X POST "https://app.utmcap.com/api/v1/sites/lead" \
-H "Authorization: Bearer $UTMCAP_KEY"
#POST /sites/shop
Connect a shop to a site.
The shop then reports its own orders, signed, instead of a browser saying a thank-you page was reached. shop_secret signs the webhooks; shop_token is the Admin API token and is needed only for the daily sweep that catches orders whose webhook never arrived. Both are stored encrypted and are never returned. Left out on a later call, each keeps the value already saved rather than clearing it.
Body
| Field | Type | Required | Notes |
|---|---|---|---|
site_id |
uuid | yes | |
shop_domain |
string | yes | The shop’s permanent .myshopify.com address. |
shop_secret |
string | The shop app’s shared secret. Required the first time. | |
shop_token |
string | Admin API access token, for reconciliation. | |
goal_key |
string | Which goal an order fires. Defaults to purchase. |
Answer — data has shop.
Errors besides the usual: no_site 400 (site_id is missing); invalid 400 (That is not a .myshopify.com address); no_secret 400 (No secret sent and none saved); not_found 404 (Not on this account); duplicate_shop 409 (That shop is connected to another site); no_key 500 (The install has no encryption key, so credentials cannot be stored); read_only_key 403.
curl -X POST "https://app.utmcap.com/api/v1/sites/shop" \
-H "Authorization: Bearer $UTMCAP_KEY" \
-H "Content-Type: application/json" \
-d '{ … }'
#PATCH /sites/shop
Which platform a store runs on.
For the platforms that post their own orders — WooCommerce, or anything else with an HTTP client. They sign with the account’s key and keep no credentials here, so there is nothing to connect; the choice is recorded so the screen opens on the right instructions. Shopify is not set this way: it owns credentials, and recording it without them would show a connected shop that is not one.
Body
| Field | Type | Required | Notes |
|---|---|---|---|
site_id |
uuid | yes | |
platform |
woocommerce, other |
yes |
Answer — data has platform.
Errors besides the usual: no_site 400 (site_id is missing); invalid 400 (That platform is not set this way); not_found 404 (Not on this account); connected 409 (A shop is connected; disconnect it first); read_only_key 403.
curl -X PATCH "https://app.utmcap.com/api/v1/sites/shop" \
-H "Authorization: Bearer $UTMCAP_KEY" \
-H "Content-Type: application/json" \
-d '{ … }'
#GET /sites/shop
What this shop’s recent webhooks did.
The last few deliveries from the connected shop and what became of each: recorded, or refused and why. Kept for an hour, because the question is asked while somebody is placing a test order — after that the conversions themselves are the record. Returns an empty list for a site with no shop.
| Parameter | Type | Required | Notes |
|---|---|---|---|
site_id |
uuid | yes |
Answer — data has deliveries.
Errors besides the usual: no_site 400 (site_id is missing).
curl "https://app.utmcap.com/api/v1/sites/shop" \
-H "Authorization: Bearer $UTMCAP_KEY"
#DELETE /sites/shop
Disconnect a shop.
| Parameter | Type | Required | Notes |
|---|---|---|---|
site_id |
uuid | yes |
Answer — data has disconnected.
Errors besides the usual: no_site 400 (site_id is missing); not_found 404 (Not on this account); read_only_key 403.
curl -X DELETE "https://app.utmcap.com/api/v1/sites/shop" \
-H "Authorization: Bearer $UTMCAP_KEY"
#Connections
DNS accounts and ad-platform sign-ins.
#GET /integrations
DNS providers, and the accounts connected.
Answer — data has providers, accounts, encryptionAvailable.
curl "https://app.utmcap.com/api/v1/integrations" \
-H "Authorization: Bearer $UTMCAP_KEY"
#PUT /integrations
Connect a DNS account, or change one.
Body
| Field | Type | Required | Notes |
|---|---|---|---|
provider |
string | yes | |
id |
uuid | Change this account instead of adding one. | |
label |
string | ||
credentials |
object | Field → value. |
Answer — data has saved, id.
Errors besides the usual: unknown_provider 400 (Unknown provider); invalid_id 400 (id is not a UUID); not_found 404 (Not on this account); read_only_key 403.
curl -X PUT "https://app.utmcap.com/api/v1/integrations" \
-H "Authorization: Bearer $UTMCAP_KEY" \
-H "Content-Type: application/json" \
-d '{ … }'
#DELETE /integrations
Disconnect a DNS account.
| Parameter | Type | Required | Notes |
|---|---|---|---|
id |
uuid | yes |
Answer — data has disconnected.
Errors besides the usual: invalid_id 400 (id is not a UUID); not_found 404 (Not on this account); read_only_key 403.
curl -X DELETE "https://app.utmcap.com/api/v1/integrations" \
-H "Authorization: Bearer $UTMCAP_KEY"
#POST /integrations/test
Test a DNS account.
Body
| Field | Type | Required | Notes |
|---|---|---|---|
id |
uuid | yes |
Answer — data has ok, message, detail, zones.
Errors besides the usual: invalid_id 400 (id is not a UUID); not_connected 400 (The account is not connected); read_only_key 403.
curl -X POST "https://app.utmcap.com/api/v1/integrations/test" \
-H "Authorization: Bearer $UTMCAP_KEY" \
-H "Content-Type: application/json" \
-d '{ … }'
#GET /integrations/zones
The zones each DNS account holds.
Answer — data has accounts.
curl "https://app.utmcap.com/api/v1/integrations/zones" \
-H "Authorization: Bearer $UTMCAP_KEY"
#GET /oauth/connections
Ad platforms connected by sign-in (Google Ads, Microsoft Ads, Meta).
Connecting and disconnecting happen in the dashboard, where the platform’s sign-in runs.
Answer — data has providers.
curl "https://app.utmcap.com/api/v1/oauth/connections" \
-H "Authorization: Bearer $UTMCAP_KEY"
#GET /oauth/{provider}/accounts
Ad accounts reachable through a platform connection.
| Parameter | Type | Required | Notes |
|---|---|---|---|
connection |
uuid | Default: the first connection. |
Answer — data has accounts.
Errors besides the usual: unsupported 404 (provider is not google_ads, microsoft_ads, meta or snapchat_ads); not_connected 400 (No connection); credentials 400 (The connection’s credentials failed); not_configured 400 (This install has no Microsoft developer token); google_refused 400 (Google refused); microsoft_refused 400 (Microsoft refused); meta_refused 400 (Meta refused); snapchat_refused 400 (Snapchat refused).
curl "https://app.utmcap.com/api/v1/oauth/YOUR_PROVIDER/accounts" \
-H "Authorization: Bearer $UTMCAP_KEY"
#GET /oauth/{provider}/pixels
Pixels on an ad account, to send conversions to.
Snapchat only. The Conversions API token is not returned by any endpoint — it is generated in Ads Manager.
| Parameter | Type | Required | Notes |
|---|---|---|---|
account |
string | The ad account whose pixels to list. Required. | |
connection |
uuid | Default: the first connection. |
Answer — data has pixels.
Errors besides the usual: unsupported 404 (provider is not snapchat_ads); no_account 400 (No account given); not_connected 400 (No connection); credentials 400 (The connection’s credentials failed); snapchat_refused 400 (Snapchat refused).
curl "https://app.utmcap.com/api/v1/oauth/YOUR_PROVIDER/pixels" \
-H "Authorization: Bearer $UTMCAP_KEY"
#GET /geo-database
Your own geo database, if connected.
Without one, your clicks use UTMCAP’s geo databases.
Answer — data has planIncludes, connected, providers, budgetPerMinute.
curl "https://app.utmcap.com/api/v1/geo-database" \
-H "Authorization: Bearer $UTMCAP_KEY"
#PUT /geo-database
Connect your own geo database, change its key, or switch it off.
A new key is tested against the provider before it is saved. Growth, Pro and Scale.
Body
| Field | Type | Required | Notes |
|---|---|---|---|
provider |
ipregistry, ipapi, ipdata, ipgeolocation, ipinfo, ip2location |
yes | |
key |
string | Required when connecting or changing provider. | |
enabled |
boolean | Default true. |
Answer — data has saved, provider, enabled.
Errors besides the usual: plan_limit 402 (The plan does not include your own geo database); invalid_provider 400 (Not one of the six providers); key_required 400 (No key given for a new provider); invalid_key 400 (The key is too long); key_refused 400 (The provider refused the key); no_encryption_key 400 (This install cannot store keys yet); read_only_key 403.
curl -X PUT "https://app.utmcap.com/api/v1/geo-database" \
-H "Authorization: Bearer $UTMCAP_KEY" \
-H "Content-Type: application/json" \
-d '{ … }'
#PATCH /geo-database
Choose which connected provider answers each field.
Per field, for your account only. null puts a field back on UTMCAP’s databases. Growth, Pro and Scale.
Body
| Field | Type | Required | Notes |
|---|---|---|---|
sources |
object | yes | Field name to provider slug, or null. For example {"proxy": "ipregistry", "city": null}. |
Answer — data has saved.
Errors besides the usual: plan_limit 402 (The plan does not include your own geo database); no_sources 400 (No fields given); unknown_field 400 (Not a geo field); not_connected 400 (Not one of your connected providers); unsupported_field 400 (That provider does not answer this field); read_only_key 403.
curl -X PATCH "https://app.utmcap.com/api/v1/geo-database" \
-H "Authorization: Bearer $UTMCAP_KEY" \
-H "Content-Type: application/json" \
-d '{ … }'
#DELETE /geo-database
Disconnect your own geo database and go back to the default.
| Parameter | Type | Required | Notes |
|---|---|---|---|
provider |
ipregistry, ipapi, ipdata, ipgeolocation, ipinfo, ip2location |
Answer — data has disconnected.
Errors besides the usual: read_only_key 403.
curl -X DELETE "https://app.utmcap.com/api/v1/geo-database" \
-H "Authorization: Bearer $UTMCAP_KEY"
#Webhooks
Events pushed to your own URL, signed, retried for a day.
#GET /webhooks
Your webhook endpoints, with the last week’s deliveries.
Answer — data has planIncludes, events, rows.
curl "https://app.utmcap.com/api/v1/webhooks" \
-H "Authorization: Bearer $UTMCAP_KEY"
#POST /webhooks
Add a webhook endpoint.
The signing secret is in the answer once and never again. Up to 10 endpoints an account.
Body
| Field | Type | Required | Notes |
|---|---|---|---|
url |
string | yes | https, on the public internet. Up to 500 characters. |
events |
conversion.created, conversion.updated, link.broken, link.unsafe, campaign.paused, plan.limit_reached[] |
yes | At least one. |
name |
string |
Answer (201) — data has id, name, url, events, active, createdAt, secret.
Errors besides the usual: plan_limit 402 (The plan does not include the API); invalid_url 400 (Not https, or not a public address); invalid_events 400 (No events, or an unknown one); too_many 400 (Already 10 endpoints); no_encryption_key 400 (This install cannot store secrets yet); read_only_key 403.
curl -X POST "https://app.utmcap.com/api/v1/webhooks" \
-H "Authorization: Bearer $UTMCAP_KEY" \
-H "Content-Type: application/json" \
-d '{ … }'
#PATCH /webhooks
Change an endpoint’s URL, events or name, or switch it off.
Body
| Field | Type | Required | Notes |
|---|---|---|---|
id |
uuid | yes | |
url |
string | ||
events |
conversion.created, conversion.updated, link.broken, link.unsafe, campaign.paused, plan.limit_reached[] |
At least one. | |
name |
string | ||
active |
boolean |
Answer — data has id, name, url, events, active.
Errors besides the usual: invalid_id 400 (id is not a UUID); not_found 404 (Not on this account); invalid_url 400 (Not https, or not a public address); invalid_events 400 (No events, or an unknown one); invalid_status 400 (active is not true or false); no_fields 400 (Nothing to change); read_only_key 403.
curl -X PATCH "https://app.utmcap.com/api/v1/webhooks" \
-H "Authorization: Bearer $UTMCAP_KEY" \
-H "Content-Type: application/json" \
-d '{ … }'
#DELETE /webhooks
Delete an endpoint and its delivery log.
| Parameter | Type | Required | Notes |
|---|---|---|---|
id |
uuid | yes |
Answer — data has id, deleted.
Errors besides the usual: invalid_id 400 (id is not a UUID); not_found 404 (Not on this account); read_only_key 403.
curl -X DELETE "https://app.utmcap.com/api/v1/webhooks" \
-H "Authorization: Bearer $UTMCAP_KEY"
#POST /webhooks/test
Send a ping event to an endpoint now.
Body
| Field | Type | Required | Notes |
|---|---|---|---|
id |
uuid | yes |
Answer — data has deliveryId, delivered, status, error.
Errors besides the usual: invalid_id 400 (id is not a UUID); not_found 404 (Not on this account); read_only_key 403.
curl -X POST "https://app.utmcap.com/api/v1/webhooks/test" \
-H "Authorization: Bearer $UTMCAP_KEY" \
-H "Content-Type: application/json" \
-d '{ … }'
#GET /webhooks/deliveries
The delivery log, newest first (30 days).
| Parameter | Type | Required | Notes |
|---|---|---|---|
limit |
integer | 1–500, default 500. | |
cursor |
string | next_cursor from the previous page. | |
webhook |
uuid | Only this endpoint. | |
status |
pending, delivered, failed |
Answer — data has rows, next_cursor.
Errors besides the usual: invalid_id 400 (webhook is not a UUID); invalid_status 400 (Unknown status).
curl "https://app.utmcap.com/api/v1/webhooks/deliveries" \
-H "Authorization: Bearer $UTMCAP_KEY"
#POST /webhooks/deliveries/redeliver
Send one delivery again now.
The same event and event id, so a receiver that deduplicates is not confused.
Body
| Field | Type | Required | Notes |
|---|---|---|---|
id |
uuid | yes |
Answer — data has id, delivered, status, error.
Errors besides the usual: invalid_id 400 (id is not a UUID); not_found 404 (Not on this account); in_progress 409 (Being sent right now); read_only_key 403.
curl -X POST "https://app.utmcap.com/api/v1/webhooks/deliveries/redeliver" \
-H "Authorization: Bearer $UTMCAP_KEY" \
-H "Content-Type: application/json" \
-d '{ … }'
#Copilot
Suggestions, changes and chat.
#GET /copilot
Open suggestions and recent changes.
| Parameter | Type | Required | Notes |
|---|---|---|---|
count |
1 |
Answer only { open }. |
Answer — data has plan, canApply, lastRunAt, total, suggestions[], changes[] — or { open } with count=1..
curl "https://app.utmcap.com/api/v1/copilot" \
-H "Authorization: Bearer $UTMCAP_KEY"
#POST /copilot
Act on a suggestion or change.
Body
| Field | Type | Required | Notes |
|---|---|---|---|
action |
refresh, apply, dismiss, dismiss_all, snooze, undo, apply_proposal, cancel_proposal, quiet_kind, keep_kind |
yes | |
id |
string | The suggestion, change or proposal. | |
variant |
network, most, all |
For apply. | |
days |
integer | For snooze. Default 7. | |
campaignId |
string | For dismiss_all: only that campaign’s. Omit to clear everything. | |
kind |
string | For quiet_kind and keep_kind: the check being answered about. |
Answer — data has Depends on the action..
Errors besides the usual: invalid 400 (Unknown action or missing id); not_found 404 (No such suggestion, change or proposal); too_soon 429 (Refreshed in the last two minutes); plan_required 403 (The plan has no one-click apply); not_open 409 (Already acted on); stale 409 (The campaign changed since); already_undone 409 (Already undone); advice_only 400 (This suggestion cannot be applied automatically); network 502 (The ad network refused); read_only_key 403.
curl -X POST "https://app.utmcap.com/api/v1/copilot" \
-H "Authorization: Bearer $UTMCAP_KEY" \
-H "Content-Type: application/json" \
-d '{ … }'
#GET /copilot/settings
How much the Copilot says.
Answer — data has settings, kinds, maxPerCampaignCeiling.
curl "https://app.utmcap.com/api/v1/copilot/settings" \
-H "Authorization: Bearer $UTMCAP_KEY"
#PUT /copilot/settings
Change how much the Copilot says.
Body
| Field | Type | Required | Notes |
|---|---|---|---|
suggestionsOn |
boolean | Off, it writes no suggestions. Asking it questions still works. | |
maxPerCampaign |
integer | Findings kept per campaign, worst first. 0 for no limit. | |
mutedKinds |
object | Kinds never to raise, from the list the GET returns. | |
quietOffersDeclined |
object | Kinds the panel has offered to switch off and been told no. |
Answer — data has settings.
Errors besides the usual: invalid 400 (maxPerCampaign is not a number); read_only_key 403.
curl -X PUT "https://app.utmcap.com/api/v1/copilot/settings" \
-H "Authorization: Bearer $UTMCAP_KEY" \
-H "Content-Type: application/json" \
-d '{ … }'
#GET /copilot/ai
The Copilot’s AI connection.
Answer — data has connection, using, message, canManage, encryption.
curl "https://app.utmcap.com/api/v1/copilot/ai" \
-H "Authorization: Bearer $UTMCAP_KEY"
#POST /copilot/ai
Connect your own AI key, choose a model, or list models.
Body
| Field | Type | Required | Notes |
|---|---|---|---|
action |
models, connect, model |
yes | |
provider |
anthropic, openai, gemini, openrouter |
||
model |
string | ||
apiKey |
string |
Answer — data has { models } for models, { connected } for connect, { model } for model..
Errors besides the usual: invalid 400 (Unknown action or provider, no key, or a model not offered); not_configured 500 (This install cannot store keys yet); ai_error 502 (The AI provider refused); read_only_key 403.
curl -X POST "https://app.utmcap.com/api/v1/copilot/ai" \
-H "Authorization: Bearer $UTMCAP_KEY" \
-H "Content-Type: application/json" \
-d '{ … }'
#DELETE /copilot/ai
Remove your own AI key.
Answer — data has removed.
Errors besides the usual: read_only_key 403.
curl -X DELETE "https://app.utmcap.com/api/v1/copilot/ai" \
-H "Authorization: Bearer $UTMCAP_KEY"
#GET /copilot/chat
Whether Copilot chat is available.
Answer — data has available, using, message.
curl "https://app.utmcap.com/api/v1/copilot/chat" \
-H "Authorization: Bearer $UTMCAP_KEY"
#POST /copilot/chat
Ask the Copilot a question about your account.
A read-only key may ask; it is never offered changes to apply.
Body
| Field | Type | Required | Notes |
|---|---|---|---|
messages |
object[] | yes | The conversation so far, ending with the question. The last 12 turns are used. |
conversation_id |
string | Carry on a stored conversation. Left out, a new one is started and its id comes back. |
Answer — data has answer, lookups, proposals, conversationId, available, using, message.
Errors besides the usual: invalid 400 (The conversation does not end with a question); ai_error 502 (The AI provider refused).
curl -X POST "https://app.utmcap.com/api/v1/copilot/chat" \
-H "Authorization: Bearer $UTMCAP_KEY" \
-H "Content-Type: application/json" \
-d '{ … }'
#GET /copilot/attachables
What a question can be about.
Campaigns, offers, landing pages, flows, traffic sources, networks, rules, tracking domains and short links, names only, newest first, up to 300 of each. With ?q=, up to 20 of each whose name contains it, among all of them.
Answer — data has rows, more.
curl "https://app.utmcap.com/api/v1/copilot/attachables" \
-H "Authorization: Bearer $UTMCAP_KEY"
#GET /copilot/conversations
Your own stored conversations, most recent first.
Only your own. There is no way to read another person’s, and no role that widens it.
Answer — data has conversations.
curl "https://app.utmcap.com/api/v1/copilot/conversations" \
-H "Authorization: Bearer $UTMCAP_KEY"
#GET /copilot/conversations/{id}
Reopen one of your conversations.
A conversation that is not yours reads as an empty one rather than as a refusal.
Answer — data has messages.
Errors besides the usual: invalid 400 (Not a conversation id).
curl "https://app.utmcap.com/api/v1/copilot/conversations/YOUR_ID" \
-H "Authorization: Bearer $UTMCAP_KEY"
#DELETE /copilot/conversations/{id}
Delete one of your conversations.
Conversations do not expire, so this is the only way one goes away.
Answer — data has deleted.
Errors besides the usual: invalid 400 (Not a conversation id); not_yours 403 (An API key has no person behind it); read_only_key 403.
curl -X DELETE "https://app.utmcap.com/api/v1/copilot/conversations/YOUR_ID" \
-H "Authorization: Bearer $UTMCAP_KEY"
#Account
Settings, plan, alerts and the edge.
#GET /alerts
What needs attention on the account.
Answer — data has alerts, count, checkedAt, services.
curl "https://app.utmcap.com/api/v1/alerts" \
-H "Authorization: Bearer $UTMCAP_KEY"
#GET /plan
The plan, and how much of each allowance is used.
Answer — data has plan, status, period, cancelAtPeriodEnd, currentPeriodEnd, allowances, includes.
curl "https://app.utmcap.com/api/v1/plan" \
-H "Authorization: Bearer $UTMCAP_KEY"
#GET /plan/usage
Usage in detail: by day, forecast, team and integrations.
Answer — data has The /plan fields plus detail { resetsAt, daily, forecast, team, integrations, copilot, plan }..
curl "https://app.utmcap.com/api/v1/plan/usage" \
-H "Authorization: Bearer $UTMCAP_KEY"
#GET /settings
Account name, currency, time zone and formats.
Answer — data has organizationName, siteKey, currency, timezone, dateFormat, timeFormat.
curl "https://app.utmcap.com/api/v1/settings" \
-H "Authorization: Bearer $UTMCAP_KEY"
#PATCH /settings
Change the account’s currency, time zone and formats.
currency, timezone and the formats are replaced: one left out goes back to its default. organization_name is left alone when not sent.
Body
| Field | Type | Required | Notes |
|---|---|---|---|
currency |
string | Three letters. | |
timezone |
string | An IANA time zone. | |
date_format |
YYYY-MM-DD, DD/MM/YYYY, MM/DD/YYYY, DD MMM YYYY |
||
time_format |
12h, 24h |
||
organization_name |
string | Up to 200 characters. |
Answer — data has currency, timezone, dateFormat, timeFormat, organizationName.
Errors besides the usual: invalid_currency 400 (Unknown currency); invalid_timezone 400 (Unknown time zone); not_found 404 (No account); bad_body 400 (Body is not a JSON object); read_only_key 403.
curl -X PATCH "https://app.utmcap.com/api/v1/settings" \
-H "Authorization: Bearer $UTMCAP_KEY" \
-H "Content-Type: application/json" \
-d '{ … }'
#GET /edge
Whether campaigns are published to the edge.
Answer — data has configured, routableCampaigns, campaignsWithoutDomain, hint.
curl "https://app.utmcap.com/api/v1/edge" \
-H "Authorization: Bearer $UTMCAP_KEY"
#POST /edge
Publish every campaign to the edge now.
Answer — data has configured, published, removed, failed, rules, rulesError, tuningError, ms.
Errors besides the usual: read_only_key 403.
curl -X POST "https://app.utmcap.com/api/v1/edge" \
-H "Authorization: Bearer $UTMCAP_KEY"
Tracking tips by email
Practical tips on tracking, attribution and ad costs, from the people who build UTMCAP.
Unsubscribe any time. Privacy policy