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.
Answer — data has id, deleted.
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"
#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); 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/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 /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 |
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. | |
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, pages, countries, devices.
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/{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.
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 switched-off signals.
A field left out keeps its value.
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. |
Answer — data has threshold, disabled.
Errors besides the usual: 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"
#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"
#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 or meta); 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).
curl "https://app.utmcap.com/api/v1/oauth/YOUR_PROVIDER/accounts" \
-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 |
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 four 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 '{ … }'
#DELETE /geo-database
Disconnect your own geo database and go back to the default.
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, snooze, undo, apply_proposal, cancel_proposal |
yes | |
id |
string | The suggestion, change or proposal. | |
variant |
network, most, all |
For apply. | |
days |
integer | For snooze. Default 7. |
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/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. |
Answer — data has answer, lookups, proposals, 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 '{ … }'
#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, ms.
Errors besides the usual: read_only_key 403.
curl -X POST "https://app.utmcap.com/api/v1/edge" \
-H "Authorization: Bearer $UTMCAP_KEY"