API reference
Meta co-pilot
The state of the Meta co-pilot on the company's ad account, its alerts (lost reactions, disapprovals, spend spikes) and the emergency stop. Configuring the mandate is only possible in the app.
Endpoints
-
GET
/meta-copilotGet the co-pilot state -
PATCH
/meta-copilot/mandateUpdate the co-pilot mandate -
GET
/meta-copilot/alertsList co-pilot alerts -
POST
/meta-copilot/stopExecute the emergency stop -
GET
/meta-copilot/opportunitiesList co-pilot opportunities -
POST
/meta-copilot/opportunities/{opportunity}/acceptLet the co-pilot act on an opportunity -
POST
/meta-copilot/opportunities/{opportunity}/dismissDismiss an opportunity -
GET
/meta-copilot/approvalsList co-pilot proposals -
POST
/meta-copilot/approvals/{approval}/approveApprove a proposal -
POST
/meta-copilot/approvals/{approval}/rejectReject a proposal -
GET
/meta-copilot/timelineGet the timeline -
GET
/meta-copilot/digestsList weekly digests -
GET
/meta-copilot/briefingsList morning briefings -
GET
/meta-copilot/revenueGet the revenue bridge -
GET
/meta-copilot/statsGet the campaign figures -
GET
/meta-copilot/crewGet the co-pilot team -
GET
/meta-copilot/valueGet what the co-pilot delivered -
GET
/meta-copilot/milestonesList milestones -
GET
/meta-copilot/benchmarkYou vs. comparable businesses -
GET
/meta-copilot/seasonalityGet the seasonality calendar -
POST
/meta-copilot/tips/{tip}/respondRespond to today's tip -
GET
/meta-copilot/competitorsList followed competitors -
POST
/meta-copilot/competitorsFollow a competitor -
DELETE
/meta-copilot/competitors/{competitor}Unfollow a competitor -
POST
/meta-copilot/competitors/ads/{ad}/variantMake a variant on a competitor ad -
GET
/meta-copilot/ads/{ad}/previewPreview of an ad -
GET
/meta-copilot/imagesList images in the library -
GET
/meta-copilot/starterGet the state of a new campaign -
PUT
/meta-copilot/starter/intakeSave answers to the start questions -
POST
/meta-copilot/starter/proposePropose a new campaign -
POST
/meta-copilot/imagesUpload an image -
DELETE
/meta-copilot/images/{image}Delete an image
Get the co-pilot state
/api/v1/meta-copilot
The mode (off, shadow, co-pilot or autopilot), the profile, the goal, the monthly budget and the counts of open alerts and pending proposals. 404 when the company has no Meta ad account.
- Scope
-
meta_copilot.read— Read the state and alerts of the Meta co-pilot - Required feature
meta_copilot
Example request
curl "https://app.klantly.com/api/v1/meta-copilot" \
-H "Authorization: Bearer $KLANTLY_API_KEY"$client = new \GuzzleHttp\Client([
'base_uri' => 'https://app.klantly.com/api/v1/',
'headers' => ['Authorization' => 'Bearer ' . getenv('KLANTLY_API_KEY')],
]);
$response = $client->request('GET', 'meta-copilot');
$data = json_decode((string) $response->getBody(), true)['data'];const response = await fetch('https://app.klantly.com/api/v1/meta-copilot', {
headers: {
Authorization: `Bearer ${process.env.KLANTLY_API_KEY}`,
},
});
const { data } = await response.json();import os
import requests
response = requests.get(
"https://app.klantly.com/api/v1/meta-copilot",
headers={
"Authorization": f"Bearer {os.environ['KLANTLY_API_KEY']}",
},
)
data = response.json()["data"]Response 200
{
"data": {
"object": "meta_copilot",
"ad_account_id": "act_123456789",
"ad_account_name": "Veranda BV",
"currency": "EUR",
"mode": "shadow",
"profile": "standard",
"goal_metric": "cost_per_quote",
"goal_value": "45.00",
"monthly_budget": "1500.00",
"autonomous_classes": [],
"open_alerts": 1,
"pending_proposals": 0,
"enabled_at": null,
"tier": "autopilot",
"ad_budget_ceiling": "7500.00",
"on_expiry": "lapse",
"stopped_at": null,
"connection_active": true,
"running": {
"state": "running",
"since": null,
"kind": "operational"
}
}
}Possible errors
-
403
insufficient_scope— This API key has no access to this action. -
404
not_found— Not found.
Update the co-pilot mandate
/api/v1/meta-copilot/mandate
Sets the profile, goal, monthly budget, daily budget ceiling, quiet days, the basis for customer data and whether an undecided proposal is applied after its deadline anyway. If the mandate becomes WIDER (more autonomous classes, a higher or removed daily ceiling, fewer quiet days, customer data allowed, expired proposals applied), confirm_widening: true is required; without it the call fails with 422 validation_failed and the explanation. Narrowing needs no confirmation. The monthly budget cannot exceed the ad budget ceiling of the tier (ad_budget_ceiling). Every widening appears as an alert in the timeline.
- Scope
-
meta_copilot.write— Halt the Meta co-pilot with the emergency stop, accept or dismiss opportunities and approve or reject proposals (configuring the mandate is only possible in the app) - Required feature
meta_copilot
Body (JSON)
| Field | Type | Description |
|---|---|---|
profile
required
|
string | The mandate profile: careful, standard or broad. one of: careful, standard, broad |
goal_metric
optional
|
string | What the co-pilot optimises for. can be empty (null) · one of: cost_per_quote, cost_per_lead, cost_per_conversion, roas |
goal_value
optional
|
number | The target value, as text with two decimals. can be empty (null) · from 0 to 1000000 |
monthly_budget
optional
|
number | The monthly ad budget agreement. can be empty (null) · from 0 to 10000000 |
max_daily_budget_euros
optional
|
number | The daily budget ceiling per campaign in euros; empty = no own ceiling. can be empty (null) · from 1 to 1000000 |
quiet_days
optional
|
array | The quiet days (monday … sunday): the co-pilot changes nothing on its own and everything goes for approval. |
crm_audience_allowed
optional
|
boolean | Whether hashed e-mail addresses and phone numbers of customers may go to Meta for audiences. Omitted = the stored choice stays. |
on_expiry
optional
|
string | What happens to a proposal not decided on in time: lapse (expires, the default) or apply (applied after the deadline anyway, within every fixed limit). one of: lapse, apply |
confirm_widening
optional
|
boolean | true to deliberately confirm a widening of the mandate. Required as soon as the input makes the mandate wider than it is now. |
notes
optional
|
string | A note for the co-pilot in plain language, such as "never advertise on Sundays". can be empty (null) · at most 2000 characters |
Example request
curl -X PATCH "https://app.klantly.com/api/v1/meta-copilot/mandate" \
-H "Authorization: Bearer $KLANTLY_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"profile": "standard",
"goal_metric": "cost_per_quote",
"goal_value": 45,
"monthly_budget": 1500,
"quiet_days": [
"sunday"
],
"on_expiry": "lapse"
}'$client = new \GuzzleHttp\Client([
'base_uri' => 'https://app.klantly.com/api/v1/',
'headers' => ['Authorization' => 'Bearer ' . getenv('KLANTLY_API_KEY')],
]);
$response = $client->request('PATCH', 'meta-copilot/mandate', [
'json' => [
'profile' => 'standard',
'goal_metric' => 'cost_per_quote',
'goal_value' => 45,
'monthly_budget' => 1500,
'quiet_days' => [
0 => 'sunday',
],
'on_expiry' => 'lapse',
],
]);
$data = json_decode((string) $response->getBody(), true)['data'];const response = await fetch('https://app.klantly.com/api/v1/meta-copilot/mandate', {
method: 'PATCH',
headers: {
Authorization: `Bearer ${process.env.KLANTLY_API_KEY}`,
'Content-Type': 'application/json',
},
body: JSON.stringify({
"profile": "standard",
"goal_metric": "cost_per_quote",
"goal_value": 45,
"monthly_budget": 1500,
"quiet_days": [
"sunday"
],
"on_expiry": "lapse"
}),
});
const { data } = await response.json();import os
import requests
response = requests.patch(
"https://app.klantly.com/api/v1/meta-copilot/mandate",
headers={
"Authorization": f"Bearer {os.environ['KLANTLY_API_KEY']}",
},
json={
"profile": "standard",
"goal_metric": "cost_per_quote",
"goal_value": 45,
"monthly_budget": 1500,
"quiet_days": [
"sunday"
],
"on_expiry": "lapse"
},
)
data = response.json()["data"]Response 200
{
"data": {
"object": "meta_copilot",
"ad_account_id": "act_123456789",
"ad_account_name": "Veranda BV",
"currency": "EUR",
"mode": "shadow",
"profile": "standard",
"goal_metric": "cost_per_quote",
"goal_value": "45.00",
"monthly_budget": "1500.00",
"autonomous_classes": [],
"open_alerts": 1,
"pending_proposals": 0,
"enabled_at": null,
"tier": "autopilot",
"ad_budget_ceiling": "7500.00",
"on_expiry": "lapse",
"stopped_at": null,
"connection_active": true,
"running": {
"state": "running",
"since": null,
"kind": "operational"
}
}
}Possible errors
-
403
insufficient_scope— This API key has no access to this action. -
422
validation_failed— The input is invalid. -
422
unknown_field— The input contains an unknown field. -
415
unsupported_media_type— This format is not supported. -
413
payload_too_large— The request body is too large. -
404
not_found— Not found.
List co-pilot alerts
/api/v1/meta-copilot/alerts
The alerts from the watchers and the engagement monitoring, newest first. Filter by status or severity. Title and detail are in the Accept-Language language.
- Scope
-
meta_copilot.read— Read the state and alerts of the Meta co-pilot - Required feature
meta_copilot
Query parameters
| Name | Type | Description |
|---|---|---|
limit
|
integer | Number of results per page. from 1 to 100 · default: 50 |
cursor
|
string | The next_cursor or prev_cursor from meta of the previous response. |
filter[status]
|
string | Only alerts with this status: open or resolved. one of: open, resolved |
filter[severity]
|
string | Only alerts with this severity: info, warning or critical. one of: info, warning, critical |
Example request
curl "https://app.klantly.com/api/v1/meta-copilot/alerts?filter[status]=open" \
-H "Authorization: Bearer $KLANTLY_API_KEY"$client = new \GuzzleHttp\Client([
'base_uri' => 'https://app.klantly.com/api/v1/',
'headers' => ['Authorization' => 'Bearer ' . getenv('KLANTLY_API_KEY')],
]);
$response = $client->request('GET', 'meta-copilot/alerts', [
'query' => [
'filter[status]' => 'open',
],
]);
$data = json_decode((string) $response->getBody(), true)['data'];const response = await fetch('https://app.klantly.com/api/v1/meta-copilot/alerts?filter[status]=open', {
headers: {
Authorization: `Bearer ${process.env.KLANTLY_API_KEY}`,
},
});
const { data } = await response.json();import os
import requests
response = requests.get(
"https://app.klantly.com/api/v1/meta-copilot/alerts",
headers={
"Authorization": f"Bearer {os.environ['KLANTLY_API_KEY']}",
},
params={
"filter[status]": "open"
},
)
data = response.json()["data"]Response 200
The response is a list with cursor pagination: data contains the objects, meta the pagination.
{
"data": [
{
"object": "meta_copilot_alert",
"id": "4821",
"kind": "engagement_post_replaced",
"severity": "critical",
"status": "open",
"title": "Advertentie \"Veranda zomer\" hangt aan een andere post: reacties en likes lopen niet meer mee",
"detail": "De post waarop 143 reacties, 27 opmerkingen en 9 keer delen stonden, wordt niet meer geadverteerd.",
"cause": "external",
"target_id": "120210000000000001",
"occurrences": 1,
"first_seen_at": null,
"last_seen_at": null,
"resolved_at": null,
"created_at": "2026-09-14T10:15:00Z",
"updated_at": "2026-09-14T10:15:00Z"
}
],
"meta": {
"limit": 50,
"next_cursor": "eyJpZCI6IjlkM2Y2YzFlIn0",
"prev_cursor": null
}
}Possible errors
-
403
insufficient_scope— This API key has no access to this action. -
404
not_found— Not found. -
422
validation_failed— The input is invalid.
Execute the emergency stop
/api/v1/meta-copilot/stop
Switches the co-pilot off: open proposals and open opportunities are withdrawn and rules it installed at Meta are disabled. Campaigns keep running as they are. 409 conflict when the co-pilot is already off.
- Scope
-
meta_copilot.write— Halt the Meta co-pilot with the emergency stop, accept or dismiss opportunities and approve or reject proposals (configuring the mandate is only possible in the app) - Required feature
meta_copilot
Send an Idempotency-Key and a retry after a timeout will never create a duplicate record.
Example request
curl -X POST "https://app.klantly.com/api/v1/meta-copilot/stop" \
-H "Authorization: Bearer $KLANTLY_API_KEY" \
-H "Idempotency-Key: 6f1c2d3e-4a5b-4c6d-8e7f-9a0b1c2d3e4f"$client = new \GuzzleHttp\Client([
'base_uri' => 'https://app.klantly.com/api/v1/',
'headers' => ['Authorization' => 'Bearer ' . getenv('KLANTLY_API_KEY')],
]);
$response = $client->request('POST', 'meta-copilot/stop', [
'headers' => [
'Idempotency-Key' => '6f1c2d3e-4a5b-4c6d-8e7f-9a0b1c2d3e4f',
],
]);
$data = json_decode((string) $response->getBody(), true)['data'];const response = await fetch('https://app.klantly.com/api/v1/meta-copilot/stop', {
method: 'POST',
headers: {
Authorization: `Bearer ${process.env.KLANTLY_API_KEY}`,
'Idempotency-Key': '6f1c2d3e-4a5b-4c6d-8e7f-9a0b1c2d3e4f',
},
});
const { data } = await response.json();import os
import requests
response = requests.post(
"https://app.klantly.com/api/v1/meta-copilot/stop",
headers={
"Authorization": f"Bearer {os.environ['KLANTLY_API_KEY']}",
"Idempotency-Key": "6f1c2d3e-4a5b-4c6d-8e7f-9a0b1c2d3e4f",
},
)
data = response.json()["data"]Response 200
{
"data": {
"object": "meta_copilot",
"ad_account_id": "act_123456789",
"ad_account_name": "Veranda BV",
"currency": "EUR",
"mode": "shadow",
"profile": "standard",
"goal_metric": "cost_per_quote",
"goal_value": "45.00",
"monthly_budget": "1500.00",
"autonomous_classes": [],
"open_alerts": 1,
"pending_proposals": 0,
"enabled_at": null,
"tier": "autopilot",
"ad_budget_ceiling": "7500.00",
"on_expiry": "lapse",
"stopped_at": null,
"connection_active": true,
"running": {
"state": "running",
"since": null,
"kind": "operational"
}
}
}Possible errors
-
403
insufficient_scope— This API key has no access to this action. -
404
not_found— Not found. -
409
conflict— This conflicts with the current state. -
422
idempotency_key_reused— This Idempotency-Key was already used for a different request. -
409
idempotency_in_progress— A request with this Idempotency-Key is still in progress.
List co-pilot opportunities
/api/v1/meta-copilot/opportunities
What the opportunity engine and the co-pilot itself found: wasted spend, creative fatigue, saturated audiences, budget concentration, placements that deliver nothing, quotes that never win, a funnel that drops off, a CPL above the Klantly median. Newest first; all statuses by default. Title and body in the Accept-Language language.
- Scope
-
meta_copilot.read— Read the state and alerts of the Meta co-pilot - Required feature
meta_copilot
Query parameters
| Name | Type | Description |
|---|---|---|
limit
|
integer | Number of results per page. from 1 to 100 · default: 50 |
cursor
|
string | The next_cursor or prev_cursor from meta of the previous response. |
filter[status]
|
string | Only opportunities with this status: open, accepted, done, dismissed or expired. one of: open, accepted, done, dismissed, expired |
Example request
curl "https://app.klantly.com/api/v1/meta-copilot/opportunities?filter[status]=open" \
-H "Authorization: Bearer $KLANTLY_API_KEY"$client = new \GuzzleHttp\Client([
'base_uri' => 'https://app.klantly.com/api/v1/',
'headers' => ['Authorization' => 'Bearer ' . getenv('KLANTLY_API_KEY')],
]);
$response = $client->request('GET', 'meta-copilot/opportunities', [
'query' => [
'filter[status]' => 'open',
],
]);
$data = json_decode((string) $response->getBody(), true)['data'];const response = await fetch('https://app.klantly.com/api/v1/meta-copilot/opportunities?filter[status]=open', {
headers: {
Authorization: `Bearer ${process.env.KLANTLY_API_KEY}`,
},
});
const { data } = await response.json();import os
import requests
response = requests.get(
"https://app.klantly.com/api/v1/meta-copilot/opportunities",
headers={
"Authorization": f"Bearer {os.environ['KLANTLY_API_KEY']}",
},
params={
"filter[status]": "open"
},
)
data = response.json()["data"]Response 200
The response is a list with cursor pagination: data contains the objects, meta the pagination.
{
"data": [
{
"object": "meta_copilot_opportunity",
"id": "318",
"source": "signal",
"kind": "wasted_spend",
"status": "open",
"title": "\"Retargeting winter\" gaf € 184,20 uit zonder één conversie",
"body": "In de laatste 30 dagen is er € 184,20 besteed en zijn er 212 klikken geweest, maar geen enkele conversie.",
"impact_cents": 18420,
"confidence": "high",
"reversible": true,
"actionable": true,
"reuse_post": null,
"image_id": null,
"target_level": "adset",
"target_id": "120210000000000001",
"target_name": "Retargeting winter",
"dismissed_reason": null,
"action_id": null,
"variant": {
"reference_ad_id": "120210000000000009",
"headline": "Veranda op maat, binnen 4 weken geplaatst",
"primary_text": "Langer buiten zitten zonder gedoe? Onze aluminium veranda wordt op maat gemaakt en binnen vier weken geplaatst. Vraag vandaag een vrijblijvende offerte aan.",
"description": "Op maat gemaakt",
"cta_type": "GET_QUOTE"
},
"blueprint": {
"branch": "veranda",
"city": "Nijmegen",
"radius_km": 40,
"age_min": 30,
"age_max": 65,
"daily_budget_cents": 1500,
"has_pixel": true,
"landing_url": "https://configurator.klantly.com/offerte-aanvragen/01k7x?utm_source=facebook",
"image_id": "12",
"headline": "Veranda op maat van Veranda Boys",
"primary_text": "Langer buiten zitten, ook in het najaar? Veranda Boys maakt veranda's op maat. Vraag vrijblijvend een offerte aan.",
"description": "Vrijblijvende offerte",
"cta_type": "GET_QUOTE",
"peer_accounts": 7,
"peer_median_cpl": "31.20"
},
"decided_at": null,
"expires_at": null,
"created_at": "2026-09-14T10:15:00Z",
"updated_at": "2026-09-14T10:15:00Z"
}
],
"meta": {
"limit": 50,
"next_cursor": "eyJpZCI6IjlkM2Y2YzFlIn0",
"prev_cursor": null
}
}Possible errors
-
403
insufficient_scope— This API key has no access to this action. -
404
not_found— Not found. -
422
validation_failed— The input is invalid.
Let the co-pilot act on an opportunity
/api/v1/meta-copilot/opportunities/{opportunity}/accept
"Let the co-pilot do this." If the opportunity carries an action and the co-pilot is not in shadow mode, the action is applied right away within the mandate; otherwise it becomes the next step in the strategy. 409 conflict when the opportunity is no longer open.
- Scope
-
meta_copilot.write— Halt the Meta co-pilot with the emergency stop, accept or dismiss opportunities and approve or reject proposals (configuring the mandate is only possible in the app) - Required feature
meta_copilot
Send an Idempotency-Key and a retry after a timeout will never create a duplicate record.
Path parameters
| Name | Type | Description |
|---|---|---|
opportunity required |
string | The id of the opportunity. |
Example request
curl -X POST "https://app.klantly.com/api/v1/meta-copilot/opportunities/9d3f6c1e-4b2a-4c8e-9f1a-2b3c4d5e6f70/accept" \
-H "Authorization: Bearer $KLANTLY_API_KEY" \
-H "Idempotency-Key: 6f1c2d3e-4a5b-4c6d-8e7f-9a0b1c2d3e4f"$client = new \GuzzleHttp\Client([
'base_uri' => 'https://app.klantly.com/api/v1/',
'headers' => ['Authorization' => 'Bearer ' . getenv('KLANTLY_API_KEY')],
]);
$response = $client->request('POST', 'meta-copilot/opportunities/9d3f6c1e-4b2a-4c8e-9f1a-2b3c4d5e6f70/accept', [
'headers' => [
'Idempotency-Key' => '6f1c2d3e-4a5b-4c6d-8e7f-9a0b1c2d3e4f',
],
]);
$data = json_decode((string) $response->getBody(), true)['data'];const response = await fetch('https://app.klantly.com/api/v1/meta-copilot/opportunities/9d3f6c1e-4b2a-4c8e-9f1a-2b3c4d5e6f70/accept', {
method: 'POST',
headers: {
Authorization: `Bearer ${process.env.KLANTLY_API_KEY}`,
'Idempotency-Key': '6f1c2d3e-4a5b-4c6d-8e7f-9a0b1c2d3e4f',
},
});
const { data } = await response.json();import os
import requests
response = requests.post(
"https://app.klantly.com/api/v1/meta-copilot/opportunities/9d3f6c1e-4b2a-4c8e-9f1a-2b3c4d5e6f70/accept",
headers={
"Authorization": f"Bearer {os.environ['KLANTLY_API_KEY']}",
"Idempotency-Key": "6f1c2d3e-4a5b-4c6d-8e7f-9a0b1c2d3e4f",
},
)
data = response.json()["data"]Response 200
{
"data": {
"object": "meta_copilot_opportunity",
"id": "318",
"source": "signal",
"kind": "wasted_spend",
"status": "open",
"title": "\"Retargeting winter\" gaf € 184,20 uit zonder één conversie",
"body": "In de laatste 30 dagen is er € 184,20 besteed en zijn er 212 klikken geweest, maar geen enkele conversie.",
"impact_cents": 18420,
"confidence": "high",
"reversible": true,
"actionable": true,
"reuse_post": null,
"image_id": null,
"target_level": "adset",
"target_id": "120210000000000001",
"target_name": "Retargeting winter",
"dismissed_reason": null,
"action_id": null,
"variant": {
"reference_ad_id": "120210000000000009",
"headline": "Veranda op maat, binnen 4 weken geplaatst",
"primary_text": "Langer buiten zitten zonder gedoe? Onze aluminium veranda wordt op maat gemaakt en binnen vier weken geplaatst. Vraag vandaag een vrijblijvende offerte aan.",
"description": "Op maat gemaakt",
"cta_type": "GET_QUOTE"
},
"blueprint": {
"branch": "veranda",
"city": "Nijmegen",
"radius_km": 40,
"age_min": 30,
"age_max": 65,
"daily_budget_cents": 1500,
"has_pixel": true,
"landing_url": "https://configurator.klantly.com/offerte-aanvragen/01k7x?utm_source=facebook",
"image_id": "12",
"headline": "Veranda op maat van Veranda Boys",
"primary_text": "Langer buiten zitten, ook in het najaar? Veranda Boys maakt veranda's op maat. Vraag vrijblijvend een offerte aan.",
"description": "Vrijblijvende offerte",
"cta_type": "GET_QUOTE",
"peer_accounts": 7,
"peer_median_cpl": "31.20"
},
"decided_at": null,
"expires_at": null,
"created_at": "2026-09-14T10:15:00Z",
"updated_at": "2026-09-14T10:15:00Z"
}
}Possible errors
-
403
insufficient_scope— This API key has no access to this action. -
404
not_found— Not found. -
409
conflict— This conflicts with the current state. -
422
idempotency_key_reused— This Idempotency-Key was already used for a different request. -
409
idempotency_in_progress— A request with this Idempotency-Key is still in progress.
Dismiss an opportunity
/api/v1/meta-copilot/opportunities/{opportunity}/dismiss
With self=true you record that you will handle it yourself; otherwise it is "not relevant". The reason is fed back to the co-pilot as a learning, and the same opportunity stays quiet for thirty days. 409 conflict when the opportunity is no longer open.
- Scope
-
meta_copilot.write— Halt the Meta co-pilot with the emergency stop, accept or dismiss opportunities and approve or reject proposals (configuring the mandate is only possible in the app) - Required feature
meta_copilot
Send an Idempotency-Key and a retry after a timeout will never create a duplicate record.
Path parameters
| Name | Type | Description |
|---|---|---|
opportunity required |
string | The id of the opportunity. |
Example request
curl -X POST "https://app.klantly.com/api/v1/meta-copilot/opportunities/9d3f6c1e-4b2a-4c8e-9f1a-2b3c4d5e6f70/dismiss" \
-H "Authorization: Bearer $KLANTLY_API_KEY" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: 6f1c2d3e-4a5b-4c6d-8e7f-9a0b1c2d3e4f" \
-d '{
"reason": "Deze campagne stopt eind van de maand toch.",
"self": false
}'$client = new \GuzzleHttp\Client([
'base_uri' => 'https://app.klantly.com/api/v1/',
'headers' => ['Authorization' => 'Bearer ' . getenv('KLANTLY_API_KEY')],
]);
$response = $client->request('POST', 'meta-copilot/opportunities/9d3f6c1e-4b2a-4c8e-9f1a-2b3c4d5e6f70/dismiss', [
'headers' => [
'Idempotency-Key' => '6f1c2d3e-4a5b-4c6d-8e7f-9a0b1c2d3e4f',
],
'json' => [
'reason' => 'Deze campagne stopt eind van de maand toch.',
'self' => false,
],
]);
$data = json_decode((string) $response->getBody(), true)['data'];const response = await fetch('https://app.klantly.com/api/v1/meta-copilot/opportunities/9d3f6c1e-4b2a-4c8e-9f1a-2b3c4d5e6f70/dismiss', {
method: 'POST',
headers: {
Authorization: `Bearer ${process.env.KLANTLY_API_KEY}`,
'Content-Type': 'application/json',
'Idempotency-Key': '6f1c2d3e-4a5b-4c6d-8e7f-9a0b1c2d3e4f',
},
body: JSON.stringify({
"reason": "Deze campagne stopt eind van de maand toch.",
"self": false
}),
});
const { data } = await response.json();import os
import requests
response = requests.post(
"https://app.klantly.com/api/v1/meta-copilot/opportunities/9d3f6c1e-4b2a-4c8e-9f1a-2b3c4d5e6f70/dismiss",
headers={
"Authorization": f"Bearer {os.environ['KLANTLY_API_KEY']}",
"Idempotency-Key": "6f1c2d3e-4a5b-4c6d-8e7f-9a0b1c2d3e4f",
},
json={
"reason": "Deze campagne stopt eind van de maand toch.",
"self": False
},
)
data = response.json()["data"]Response 200
{
"data": {
"object": "meta_copilot_opportunity",
"id": "318",
"source": "signal",
"kind": "wasted_spend",
"status": "open",
"title": "\"Retargeting winter\" gaf € 184,20 uit zonder één conversie",
"body": "In de laatste 30 dagen is er € 184,20 besteed en zijn er 212 klikken geweest, maar geen enkele conversie.",
"impact_cents": 18420,
"confidence": "high",
"reversible": true,
"actionable": true,
"reuse_post": null,
"image_id": null,
"target_level": "adset",
"target_id": "120210000000000001",
"target_name": "Retargeting winter",
"dismissed_reason": null,
"action_id": null,
"variant": {
"reference_ad_id": "120210000000000009",
"headline": "Veranda op maat, binnen 4 weken geplaatst",
"primary_text": "Langer buiten zitten zonder gedoe? Onze aluminium veranda wordt op maat gemaakt en binnen vier weken geplaatst. Vraag vandaag een vrijblijvende offerte aan.",
"description": "Op maat gemaakt",
"cta_type": "GET_QUOTE"
},
"blueprint": {
"branch": "veranda",
"city": "Nijmegen",
"radius_km": 40,
"age_min": 30,
"age_max": 65,
"daily_budget_cents": 1500,
"has_pixel": true,
"landing_url": "https://configurator.klantly.com/offerte-aanvragen/01k7x?utm_source=facebook",
"image_id": "12",
"headline": "Veranda op maat van Veranda Boys",
"primary_text": "Langer buiten zitten, ook in het najaar? Veranda Boys maakt veranda's op maat. Vraag vrijblijvend een offerte aan.",
"description": "Vrijblijvende offerte",
"cta_type": "GET_QUOTE",
"peer_accounts": 7,
"peer_median_cpl": "31.20"
},
"decided_at": null,
"expires_at": null,
"created_at": "2026-09-14T10:15:00Z",
"updated_at": "2026-09-14T10:15:00Z"
}
}Possible errors
-
403
insufficient_scope— This API key has no access to this action. -
422
validation_failed— The input is invalid. -
422
unknown_field— The input contains an unknown field. -
415
unsupported_media_type— This format is not supported. -
413
payload_too_large— The request body is too large. -
404
not_found— Not found. -
409
conflict— This conflicts with the current state. -
422
idempotency_key_reused— This Idempotency-Key was already used for a different request. -
409
idempotency_in_progress— A request with this Idempotency-Key is still in progress.
List co-pilot proposals
/api/v1/meta-copilot/approvals
The proposals waiting for a decision (default filter[status]=proposed), or with a filter the shadow-mode intentions and what was applied, dismissed, expired or failed. Without payload and without staff names.
- Scope
-
meta_copilot.read— Read the state and alerts of the Meta co-pilot - Required feature
meta_copilot
Query parameters
| Name | Type | Description |
|---|---|---|
limit
|
integer | Number of results per page. from 1 to 100 · default: 50 |
cursor
|
string | The next_cursor or prev_cursor from meta of the previous response. |
filter[status]
|
string | Only proposals with this status: proposed, simulated, applied, dismissed, expired or failed. Without a filter: proposed. one of: proposed, simulated, applied, dismissed, expired, failed |
Example request
curl "https://app.klantly.com/api/v1/meta-copilot/approvals?filter[status]=proposed" \
-H "Authorization: Bearer $KLANTLY_API_KEY"$client = new \GuzzleHttp\Client([
'base_uri' => 'https://app.klantly.com/api/v1/',
'headers' => ['Authorization' => 'Bearer ' . getenv('KLANTLY_API_KEY')],
]);
$response = $client->request('GET', 'meta-copilot/approvals', [
'query' => [
'filter[status]' => 'proposed',
],
]);
$data = json_decode((string) $response->getBody(), true)['data'];const response = await fetch('https://app.klantly.com/api/v1/meta-copilot/approvals?filter[status]=proposed', {
headers: {
Authorization: `Bearer ${process.env.KLANTLY_API_KEY}`,
},
});
const { data } = await response.json();import os
import requests
response = requests.get(
"https://app.klantly.com/api/v1/meta-copilot/approvals",
headers={
"Authorization": f"Bearer {os.environ['KLANTLY_API_KEY']}",
},
params={
"filter[status]": "proposed"
},
)
data = response.json()["data"]Response 200
The response is a list with cursor pagination: data contains the objects, meta the pagination.
{
"data": [
{
"object": "meta_copilot_approval",
"id": "2077",
"status": "proposed",
"action_type": "adjust_budget",
"target_level": "adset",
"target_id": "120210000000000001",
"target_name": "Retargeting winter",
"title": "Budget van \"Retargeting winter\" verlagen van € 40 naar € 30 per dag",
"rationale": "De kosten per offerte liggen hier 60% boven het accountgemiddelde.",
"risk": "low",
"impact_estimate_cents": 30000,
"change": {
"kind": "budget",
"level": "adset",
"manual": false,
"from_cents": 4000,
"to_cents": 3000,
"placements": [
"audience_network"
],
"devices": [
"mobile"
],
"min": 30,
"max": 65,
"genders": [
"female"
],
"audience_name": null,
"headline": null,
"days": 14,
"template": null,
"purpose": null,
"percent": 1,
"country": "NL",
"strategy": "cost_cap",
"cap_cents": null,
"daily_budget_cents": null
},
"simulated": false,
"reuse_post": null,
"image_id": null,
"approval_deadline_at": null,
"applied_at": null,
"outcome": null,
"error": null,
"created_at": "2026-09-14T10:15:00Z",
"updated_at": "2026-09-14T10:15:00Z"
}
],
"meta": {
"limit": 50,
"next_cursor": "eyJpZCI6IjlkM2Y2YzFlIn0",
"prev_cursor": null
}
}Possible errors
-
403
insufficient_scope— This API key has no access to this action. -
404
not_found— Not found. -
422
validation_failed— The input is invalid.
Approve a proposal
/api/v1/meta-copilot/approvals/{approval}/approve
Applies the proposal at Meta within the account mandate. Only for a proposal with status proposed; otherwise 409 conflict. In shadow mode the executor refuses. On an ad variant with the same text as the original, reuse_post chooses whether the existing post is reused (default true).
- Scope
-
meta_copilot.write— Halt the Meta co-pilot with the emergency stop, accept or dismiss opportunities and approve or reject proposals (configuring the mandate is only possible in the app) - Required feature
meta_copilot
Send an Idempotency-Key and a retry after a timeout will never create a duplicate record.
Path parameters
| Name | Type | Description |
|---|---|---|
approval required |
string | The id of the proposal. |
Example request
curl -X POST "https://app.klantly.com/api/v1/meta-copilot/approvals/9d3f6c1e-4b2a-4c8e-9f1a-2b3c4d5e6f70/approve" \
-H "Authorization: Bearer $KLANTLY_API_KEY" \
-H "Idempotency-Key: 6f1c2d3e-4a5b-4c6d-8e7f-9a0b1c2d3e4f"$client = new \GuzzleHttp\Client([
'base_uri' => 'https://app.klantly.com/api/v1/',
'headers' => ['Authorization' => 'Bearer ' . getenv('KLANTLY_API_KEY')],
]);
$response = $client->request('POST', 'meta-copilot/approvals/9d3f6c1e-4b2a-4c8e-9f1a-2b3c4d5e6f70/approve', [
'headers' => [
'Idempotency-Key' => '6f1c2d3e-4a5b-4c6d-8e7f-9a0b1c2d3e4f',
],
]);
$data = json_decode((string) $response->getBody(), true)['data'];const response = await fetch('https://app.klantly.com/api/v1/meta-copilot/approvals/9d3f6c1e-4b2a-4c8e-9f1a-2b3c4d5e6f70/approve', {
method: 'POST',
headers: {
Authorization: `Bearer ${process.env.KLANTLY_API_KEY}`,
'Idempotency-Key': '6f1c2d3e-4a5b-4c6d-8e7f-9a0b1c2d3e4f',
},
});
const { data } = await response.json();import os
import requests
response = requests.post(
"https://app.klantly.com/api/v1/meta-copilot/approvals/9d3f6c1e-4b2a-4c8e-9f1a-2b3c4d5e6f70/approve",
headers={
"Authorization": f"Bearer {os.environ['KLANTLY_API_KEY']}",
"Idempotency-Key": "6f1c2d3e-4a5b-4c6d-8e7f-9a0b1c2d3e4f",
},
)
data = response.json()["data"]Response 200
{
"data": {
"object": "meta_copilot_approval",
"id": "2077",
"status": "proposed",
"action_type": "adjust_budget",
"target_level": "adset",
"target_id": "120210000000000001",
"target_name": "Retargeting winter",
"title": "Budget van \"Retargeting winter\" verlagen van € 40 naar € 30 per dag",
"rationale": "De kosten per offerte liggen hier 60% boven het accountgemiddelde.",
"risk": "low",
"impact_estimate_cents": 30000,
"change": {
"kind": "budget",
"level": "adset",
"manual": false,
"from_cents": 4000,
"to_cents": 3000,
"placements": [
"audience_network"
],
"devices": [
"mobile"
],
"min": 30,
"max": 65,
"genders": [
"female"
],
"audience_name": null,
"headline": null,
"days": 14,
"template": null,
"purpose": null,
"percent": 1,
"country": "NL",
"strategy": "cost_cap",
"cap_cents": null,
"daily_budget_cents": null
},
"simulated": false,
"reuse_post": null,
"image_id": null,
"approval_deadline_at": null,
"applied_at": null,
"outcome": null,
"error": null,
"created_at": "2026-09-14T10:15:00Z",
"updated_at": "2026-09-14T10:15:00Z"
}
}Possible errors
-
403
insufficient_scope— This API key has no access to this action. -
404
not_found— Not found. -
409
conflict— This conflicts with the current state. -
422
idempotency_key_reused— This Idempotency-Key was already used for a different request. -
409
idempotency_in_progress— A request with this Idempotency-Key is still in progress.
Reject a proposal
/api/v1/meta-copilot/approvals/{approval}/reject
Sets the proposal (or a shadow-mode intention) to dismissed. Nothing changes at Meta.
- Scope
-
meta_copilot.write— Halt the Meta co-pilot with the emergency stop, accept or dismiss opportunities and approve or reject proposals (configuring the mandate is only possible in the app) - Required feature
meta_copilot
Send an Idempotency-Key and a retry after a timeout will never create a duplicate record.
Path parameters
| Name | Type | Description |
|---|---|---|
approval required |
string | The id of the proposal. |
Example request
curl -X POST "https://app.klantly.com/api/v1/meta-copilot/approvals/9d3f6c1e-4b2a-4c8e-9f1a-2b3c4d5e6f70/reject" \
-H "Authorization: Bearer $KLANTLY_API_KEY" \
-H "Idempotency-Key: 6f1c2d3e-4a5b-4c6d-8e7f-9a0b1c2d3e4f"$client = new \GuzzleHttp\Client([
'base_uri' => 'https://app.klantly.com/api/v1/',
'headers' => ['Authorization' => 'Bearer ' . getenv('KLANTLY_API_KEY')],
]);
$response = $client->request('POST', 'meta-copilot/approvals/9d3f6c1e-4b2a-4c8e-9f1a-2b3c4d5e6f70/reject', [
'headers' => [
'Idempotency-Key' => '6f1c2d3e-4a5b-4c6d-8e7f-9a0b1c2d3e4f',
],
]);
$data = json_decode((string) $response->getBody(), true)['data'];const response = await fetch('https://app.klantly.com/api/v1/meta-copilot/approvals/9d3f6c1e-4b2a-4c8e-9f1a-2b3c4d5e6f70/reject', {
method: 'POST',
headers: {
Authorization: `Bearer ${process.env.KLANTLY_API_KEY}`,
'Idempotency-Key': '6f1c2d3e-4a5b-4c6d-8e7f-9a0b1c2d3e4f',
},
});
const { data } = await response.json();import os
import requests
response = requests.post(
"https://app.klantly.com/api/v1/meta-copilot/approvals/9d3f6c1e-4b2a-4c8e-9f1a-2b3c4d5e6f70/reject",
headers={
"Authorization": f"Bearer {os.environ['KLANTLY_API_KEY']}",
"Idempotency-Key": "6f1c2d3e-4a5b-4c6d-8e7f-9a0b1c2d3e4f",
},
)
data = response.json()["data"]Response 200
{
"data": {
"object": "meta_copilot_approval",
"id": "2077",
"status": "proposed",
"action_type": "adjust_budget",
"target_level": "adset",
"target_id": "120210000000000001",
"target_name": "Retargeting winter",
"title": "Budget van \"Retargeting winter\" verlagen van € 40 naar € 30 per dag",
"rationale": "De kosten per offerte liggen hier 60% boven het accountgemiddelde.",
"risk": "low",
"impact_estimate_cents": 30000,
"change": {
"kind": "budget",
"level": "adset",
"manual": false,
"from_cents": 4000,
"to_cents": 3000,
"placements": [
"audience_network"
],
"devices": [
"mobile"
],
"min": 30,
"max": 65,
"genders": [
"female"
],
"audience_name": null,
"headline": null,
"days": 14,
"template": null,
"purpose": null,
"percent": 1,
"country": "NL",
"strategy": "cost_cap",
"cap_cents": null,
"daily_budget_cents": null
},
"simulated": false,
"reuse_post": null,
"image_id": null,
"approval_deadline_at": null,
"applied_at": null,
"outcome": null,
"error": null,
"created_at": "2026-09-14T10:15:00Z",
"updated_at": "2026-09-14T10:15:00Z"
}
}Possible errors
-
403
insufficient_scope— This API key has no access to this action. -
404
not_found— Not found. -
409
conflict— This conflicts with the current state. -
422
idempotency_key_reused— This Idempotency-Key was already used for a different request. -
409
idempotency_in_progress— A request with this Idempotency-Key is still in progress.
Get the timeline
/api/v1/meta-copilot/timeline
Runs, changes and intentions, alerts and mode switches in one list, newest first. No cursor: at most 100 entries.
- Scope
-
meta_copilot.read— Read the state and alerts of the Meta co-pilot - Required feature
meta_copilot
Query parameters
| Name | Type | Description |
|---|---|---|
limit
|
integer | Number of entries, at most 100 (default 50). from 1 to 100 · default: 50 |
Example request
curl "https://app.klantly.com/api/v1/meta-copilot/timeline" \
-H "Authorization: Bearer $KLANTLY_API_KEY"$client = new \GuzzleHttp\Client([
'base_uri' => 'https://app.klantly.com/api/v1/',
'headers' => ['Authorization' => 'Bearer ' . getenv('KLANTLY_API_KEY')],
]);
$response = $client->request('GET', 'meta-copilot/timeline');
$data = json_decode((string) $response->getBody(), true)['data'];const response = await fetch('https://app.klantly.com/api/v1/meta-copilot/timeline', {
headers: {
Authorization: `Bearer ${process.env.KLANTLY_API_KEY}`,
},
});
const { data } = await response.json();import os
import requests
response = requests.get(
"https://app.klantly.com/api/v1/meta-copilot/timeline",
headers={
"Authorization": f"Bearer {os.environ['KLANTLY_API_KEY']}",
},
)
data = response.json()["data"]Response 200
The response is a list with cursor pagination: data contains the objects, meta the pagination.
{
"data": [
{
"object": "meta_copilot_timeline_entry",
"id": "action-2077",
"type": "action",
"at": null,
"kind": "adjust_budget",
"status": "applied",
"title": "Budget van \"Retargeting winter\" verlaagd naar € 30 per dag",
"rationale": "De kosten per offerte liggen hier 60% boven het accountgemiddelde.",
"target_name": "Retargeting winter",
"actor": "copilot",
"cause": null,
"severity": null,
"simulated": false,
"action_id": "2077",
"alert_id": null
}
],
"meta": {
"limit": 50,
"next_cursor": "eyJpZCI6IjlkM2Y2YzFlIn0",
"prev_cursor": null
}
}Possible errors
-
403
insufficient_scope— This API key has no access to this action. -
404
not_found— Not found. -
422
validation_failed— The input is invalid.
List weekly digests
/api/v1/meta-copilot/digests
The weekly summaries the co-pilot writes on Monday: spend, results, what it did and what it proposes. As data, not as text.
- Scope
-
meta_copilot.read— Read the state and alerts of the Meta co-pilot - Required feature
meta_copilot
Query parameters
| Name | Type | Description |
|---|---|---|
limit
|
integer | Number of results per page. from 1 to 100 · default: 50 |
cursor
|
string | The next_cursor or prev_cursor from meta of the previous response. |
Example request
curl "https://app.klantly.com/api/v1/meta-copilot/digests" \
-H "Authorization: Bearer $KLANTLY_API_KEY"$client = new \GuzzleHttp\Client([
'base_uri' => 'https://app.klantly.com/api/v1/',
'headers' => ['Authorization' => 'Bearer ' . getenv('KLANTLY_API_KEY')],
]);
$response = $client->request('GET', 'meta-copilot/digests');
$data = json_decode((string) $response->getBody(), true)['data'];const response = await fetch('https://app.klantly.com/api/v1/meta-copilot/digests', {
headers: {
Authorization: `Bearer ${process.env.KLANTLY_API_KEY}`,
},
});
const { data } = await response.json();import os
import requests
response = requests.get(
"https://app.klantly.com/api/v1/meta-copilot/digests",
headers={
"Authorization": f"Bearer {os.environ['KLANTLY_API_KEY']}",
},
)
data = response.json()["data"]Response 200
The response is a list with cursor pagination: data contains the objects, meta the pagination.
{
"data": [
{
"object": "meta_copilot_digest",
"id": "56",
"period_start": "2026-10-12",
"period_end": "2026-10-18",
"language": "nl",
"spend": "412.80",
"previous_spend": "398.10",
"conversions": 9,
"cost_per_conversion": "45.87",
"copilot_runs": 7,
"copilot_applied": 2,
"copilot_intentions": 0,
"outcomes": {
"improved": 1,
"neutral": 1,
"worsened": 0
},
"waiting": {
"proposals": 1,
"alerts": 0,
"opportunities": 3
},
"next_steps": [
"Nieuwe variant naast \"Veranda zomer\" testen"
],
"sent_at": null,
"created_at": "2026-09-14T10:15:00Z"
}
],
"meta": {
"limit": 50,
"next_cursor": "eyJpZCI6IjlkM2Y2YzFlIn0",
"prev_cursor": null
}
}Possible errors
-
403
insufficient_scope— This API key has no access to this action. -
404
not_found— Not found. -
422
validation_failed— The input is invalid.
List morning briefings
/api/v1/meta-copilot/briefings
One briefing per day about the day before: spend, leads, quotes and won deals from Klantly against the previous week's daily average, with the kind of day and that morning's milestones. Headline and body follow Accept-Language.
- Scope
-
meta_copilot.read— Read the state and alerts of the Meta co-pilot - Required feature
meta_copilot
Query parameters
| Name | Type | Description |
|---|---|---|
limit
|
integer | Number of results per page. from 1 to 100 · default: 50 |
cursor
|
string | The next_cursor or prev_cursor from meta of the previous response. |
Example request
curl "https://app.klantly.com/api/v1/meta-copilot/briefings" \
-H "Authorization: Bearer $KLANTLY_API_KEY"$client = new \GuzzleHttp\Client([
'base_uri' => 'https://app.klantly.com/api/v1/',
'headers' => ['Authorization' => 'Bearer ' . getenv('KLANTLY_API_KEY')],
]);
$response = $client->request('GET', 'meta-copilot/briefings');
$data = json_decode((string) $response->getBody(), true)['data'];const response = await fetch('https://app.klantly.com/api/v1/meta-copilot/briefings', {
headers: {
Authorization: `Bearer ${process.env.KLANTLY_API_KEY}`,
},
});
const { data } = await response.json();import os
import requests
response = requests.get(
"https://app.klantly.com/api/v1/meta-copilot/briefings",
headers={
"Authorization": f"Bearer {os.environ['KLANTLY_API_KEY']}",
},
)
data = response.json()["data"]Response 200
The response is a list with cursor pagination: data contains the objects, meta the pagination.
{
"data": [
{
"object": "meta_copilot_briefing",
"id": "412",
"date": "2026-10-22",
"kind": "quotes",
"headline": "Twee offerteaanvragen uit Meta gisteren.",
"body": "Besteed € 38,20 (gemiddeld € 41,10 per dag), 3 leads (gemiddeld 2,4), 2 offertes, 0 gewonnen.",
"spend": "38.20",
"avg_spend": "41.10",
"leads": 3,
"avg_leads": 2.4,
"clicks": 61,
"quotes": 2,
"won": 0,
"won_value": "0.00",
"cost_per_lead": "12.73",
"mtd_spend": "812.40",
"milestone_ids": [
"18"
],
"tip": {
"key": "frequency_high",
"category": "audience",
"actor": "copilot",
"title": "Je doelgroep ziet elke advertentie 3,4 keer",
"body": "Boven de 3 vertoningen per persoon stijgen de kosten per lead.",
"action": "Laat de co-pilot dit oppakken"
},
"pushed_at": null,
"created_at": "2026-09-14T10:15:00Z"
}
],
"meta": {
"limit": 50,
"next_cursor": "eyJpZCI6IjlkM2Y2YzFlIn0",
"prev_cursor": null
}
}Possible errors
-
403
insufficient_scope— This API key has no access to this action. -
404
not_found— Not found. -
422
validation_failed— The input is invalid.
Get the revenue bridge
/api/v1/meta-copilot/revenue
Ad spend → leads → quotes → won → revenue over the last `days` days (default 30), per campaign and per calendar month, on real Klantly revenue. Quotes only count when the campaign macro ties them to a campaign of this account.
- Scope
-
meta_copilot.read— Read the state and alerts of the Meta co-pilot - Required feature
meta_copilot
Query parameters
| Name | Type | Description |
|---|---|---|
days
|
integer | How many days back, 7 to 365 (default 30). The monthly series is always the last six calendar months. from 7 to 365 · default: 30 |
Example request
curl "https://app.klantly.com/api/v1/meta-copilot/revenue?days=30" \
-H "Authorization: Bearer $KLANTLY_API_KEY"$client = new \GuzzleHttp\Client([
'base_uri' => 'https://app.klantly.com/api/v1/',
'headers' => ['Authorization' => 'Bearer ' . getenv('KLANTLY_API_KEY')],
]);
$response = $client->request('GET', 'meta-copilot/revenue', [
'query' => [
'days' => '30',
],
]);
$data = json_decode((string) $response->getBody(), true)['data'];const response = await fetch('https://app.klantly.com/api/v1/meta-copilot/revenue?days=30', {
headers: {
Authorization: `Bearer ${process.env.KLANTLY_API_KEY}`,
},
});
const { data } = await response.json();import os
import requests
response = requests.get(
"https://app.klantly.com/api/v1/meta-copilot/revenue",
headers={
"Authorization": f"Bearer {os.environ['KLANTLY_API_KEY']}",
},
params={
"days": "30"
},
)
data = response.json()["data"]Response 200
{
"data": {
"object": "meta_copilot_revenue",
"since": "2026-09-24",
"until": "2026-10-23",
"uses_quotes": true,
"spend": "1240.50",
"clicks": 1830,
"impressions": 92400,
"leads": 74,
"quotes": 31,
"quotes_value": "142300.00",
"won": 6,
"won_value": "27850.00",
"cost_per_lead": "16.76",
"cost_per_quote": "40.02",
"cost_per_won": "206.75",
"roas": 22.45,
"revenue_source": "quotes",
"purchases": 0,
"purchase_value": "0.00",
"cost_per_purchase": null,
"meta_roas": null,
"copilot_impact": "412.00",
"campaigns": [
{
"campaign_id": "120210000000001",
"name": "Veranda voorjaar",
"spend": "812.00",
"leads": 51,
"quotes": 22,
"quotes_value": "98400.00",
"won": 4,
"won_value": "19200.00",
"cost_per_lead": "15.92",
"roas": 23.65,
"purchases": 0,
"purchase_value": "0.00",
"meta_roas": null
}
],
"months": [
{
"month": "2026-10",
"spend": "1240.50",
"leads": 74,
"quotes": 31,
"quotes_value": "142300.00",
"won": 6,
"won_value": "27850.00",
"cost_per_lead": "16.76",
"roas": 22.45,
"purchases": 0,
"purchase_value": "0.00",
"meta_roas": null
}
]
}
}Possible errors
-
403
insufficient_scope— This API key has no access to this action. -
404
not_found— Not found. -
422
validation_failed— The input is invalid.
Get the campaign figures
/api/v1/meta-copilot/stats
Spend, impressions, clicks and results over the last `days` days (7, 14, 30 or 90; default 30): totals, the same period before, per day and per campaign. Suitable for charts. The co-pilot updates the figures every morning and afternoon.
- Scope
-
meta_copilot.read— Read the state and alerts of the Meta co-pilot - Required feature
meta_copilot
Query parameters
| Name | Type | Description |
|---|---|---|
days
|
integer | How many days back: 7, 14, 30 or 90 (default 30). one of: 7, 14, 30, 90 · default: 30 |
Example request
curl "https://app.klantly.com/api/v1/meta-copilot/stats?days=30" \
-H "Authorization: Bearer $KLANTLY_API_KEY"$client = new \GuzzleHttp\Client([
'base_uri' => 'https://app.klantly.com/api/v1/',
'headers' => ['Authorization' => 'Bearer ' . getenv('KLANTLY_API_KEY')],
]);
$response = $client->request('GET', 'meta-copilot/stats', [
'query' => [
'days' => '30',
],
]);
$data = json_decode((string) $response->getBody(), true)['data'];const response = await fetch('https://app.klantly.com/api/v1/meta-copilot/stats?days=30', {
headers: {
Authorization: `Bearer ${process.env.KLANTLY_API_KEY}`,
},
});
const { data } = await response.json();import os
import requests
response = requests.get(
"https://app.klantly.com/api/v1/meta-copilot/stats",
headers={
"Authorization": f"Bearer {os.environ['KLANTLY_API_KEY']}",
},
params={
"days": "30"
},
)
data = response.json()["data"]Response 200
{
"data": {
"object": "meta_copilot_stats",
"since": "2026-09-26",
"until": "2026-10-25",
"days": 30,
"currency": "EUR",
"result_kind": "leads",
"latest_date": "2026-10-04",
"synced_at": null,
"totals": {
"spend": "412.30",
"impressions": 31840,
"clicks": 612,
"results": 23,
"leads": 23,
"purchases": 0,
"purchase_value": "0.00",
"ctr": 1.92,
"cpc": "0.67",
"cpm": "12.95",
"cost_per_result": "17.93",
"roas": null
},
"previous": {
"spend": "412.30",
"impressions": 31840,
"clicks": 612,
"results": 23,
"leads": 23,
"purchases": 0,
"purchase_value": "0.00",
"ctr": 1.92,
"cpc": "0.67",
"cpm": "12.95",
"cost_per_result": "17.93",
"roas": null
},
"daily": [
{
"date": "2026-10-25",
"spend": "14.20",
"impressions": 1080,
"clicks": 21,
"results": 1,
"leads": 1,
"purchases": 0,
"purchase_value": "0.00",
"ctr": 1.94,
"cpc": "0.68",
"cpm": "13.15",
"cost_per_result": "14.20"
}
],
"campaigns": [
{
"campaign_id": "120210000000001",
"name": "Veranda voorjaar",
"status": "ACTIVE",
"share": 64.2,
"daily_spend": [
"12.40",
"14.10"
],
"spend": "264.70",
"impressions": 20410,
"clicks": 402,
"results": 16,
"leads": 16,
"purchases": 0,
"purchase_value": "0.00",
"ctr": 1.97,
"cpc": "0.66",
"cpm": "12.97",
"cost_per_result": "16.54",
"roas": null
}
]
}
}Possible errors
-
403
insufficient_scope— This API key has no access to this action. -
404
not_found— Not found. -
422
validation_failed— The input is invalid.
Get the co-pilot team
/api/v1/meta-copilot/crew
What each co-pilot agent did last, when it gets to work again according to the schedule, and the counters for today and this week. Everything comes from real actions and the real schedule.
- Scope
-
meta_copilot.read— Read the state and alerts of the Meta co-pilot - Required feature
meta_copilot
Example request
curl "https://app.klantly.com/api/v1/meta-copilot/crew" \
-H "Authorization: Bearer $KLANTLY_API_KEY"$client = new \GuzzleHttp\Client([
'base_uri' => 'https://app.klantly.com/api/v1/',
'headers' => ['Authorization' => 'Bearer ' . getenv('KLANTLY_API_KEY')],
]);
$response = $client->request('GET', 'meta-copilot/crew');
$data = json_decode((string) $response->getBody(), true)['data'];const response = await fetch('https://app.klantly.com/api/v1/meta-copilot/crew', {
headers: {
Authorization: `Bearer ${process.env.KLANTLY_API_KEY}`,
},
});
const { data } = await response.json();import os
import requests
response = requests.get(
"https://app.klantly.com/api/v1/meta-copilot/crew",
headers={
"Authorization": f"Bearer {os.environ['KLANTLY_API_KEY']}",
},
)
data = response.json()["data"]Response 200
{
"data": {
"object": "meta_copilot_crew",
"state": "active",
"mode": "copilot",
"agents": [
{
"role": "optimizer",
"status": "ready",
"last_at": "2026-10-25T05:03:11Z",
"last_kind": "run_operational",
"last_facts": {
"status": "completed",
"tool_calls": 14,
"proposals": 2
},
"next_at": "2026-10-28T06:00:00Z",
"next_kind": "operational",
"count_7d": 3,
"week": [
0,
1,
0,
0,
1,
0,
1
]
}
],
"today": {
"checks": 14,
"campaigns": 4,
"opportunities": 1,
"proposals": 2
},
"week": {
"checks": 98,
"rounds": 3,
"proposals": 5,
"applied": 2
},
"next": {
"role": "guard",
"at": "2026-10-26T05:45:00Z"
},
"feed": [
{
"role": "analyst",
"kind": "stats_synced",
"at": "2026-10-25T04:45:12Z",
"facts": {
"campaigns": 4,
"days": 14,
"rows": 56
}
}
]
}
}Possible errors
-
403
insufficient_scope— This API key has no access to this action. -
404
not_found— Not found.
Get what the co-pilot delivered
/api/v1/meta-copilot/value
What the co-pilot did since it was switched on and what that delivered: improvements made (on its own or after approval), the measurement a week later (better, same, worse, still measuring), the estimated value, what it watched, and cost per result in the days before and since switching it on. The estimate is the expectation for each proposal, not a measurement; the comparison only appears after seven days and five results on both sides.
- Scope
-
meta_copilot.read— Read the state and alerts of the Meta co-pilot - Required feature
meta_copilot
Example request
curl "https://app.klantly.com/api/v1/meta-copilot/value" \
-H "Authorization: Bearer $KLANTLY_API_KEY"$client = new \GuzzleHttp\Client([
'base_uri' => 'https://app.klantly.com/api/v1/',
'headers' => ['Authorization' => 'Bearer ' . getenv('KLANTLY_API_KEY')],
]);
$response = $client->request('GET', 'meta-copilot/value');
$data = json_decode((string) $response->getBody(), true)['data'];const response = await fetch('https://app.klantly.com/api/v1/meta-copilot/value', {
headers: {
Authorization: `Bearer ${process.env.KLANTLY_API_KEY}`,
},
});
const { data } = await response.json();import os
import requests
response = requests.get(
"https://app.klantly.com/api/v1/meta-copilot/value",
headers={
"Authorization": f"Bearer {os.environ['KLANTLY_API_KEY']}",
},
)
data = response.json()["data"]Response 200
{
"data": {
"object": "meta_copilot_value",
"since": "2026-10-02T08:14:00Z",
"days_on": 26,
"mode": "autopilot",
"currency": "EUR",
"actions": {
"applied": 9,
"by_itself": 7,
"approved": 2,
"rolled_back": 0
},
"outcomes": {
"improved": 5,
"neutral": 1,
"worsened": 1,
"measuring": 2
},
"estimated_value": "640.00",
"watch": {
"checks_30d": 412,
"alerts": 6,
"alerts_resolved": 5
},
"comparison": {
"available": true,
"days": 26,
"result_kind": "leads",
"before": {
"spend": "812.40",
"results": 31,
"cost_per_result": "26.21"
},
"after": {
"spend": "790.10",
"results": 42,
"cost_per_result": "18.81"
},
"change_pct": -28.2
},
"recent": [
{
"id": 4182,
"title": "Budget van Veranda voorjaar met 15% verhoogd",
"action_type": "update_campaign_budget",
"at": "2026-10-24T05:12:00Z",
"by_itself": true,
"outcome": "measuring",
"rollback": false,
"impact_estimate": "120.00"
}
],
"generated_at": "2026-10-28T09:30:00Z"
}
}Possible errors
-
403
insufficient_scope— This API key has no access to this action. -
404
not_found— Not found.
List milestones
/api/v1/meta-copilot/milestones
Celebrated milestones (the silently recorded baseline of an existing account is not included), newest first: leads from Meta, won deals, the best week and consecutive weeks without wasted spend. Title and detail follow Accept-Language.
- Scope
-
meta_copilot.read— Read the state and alerts of the Meta co-pilot - Required feature
meta_copilot
Query parameters
| Name | Type | Description |
|---|---|---|
limit
|
integer | Number of results per page. from 1 to 100 · default: 50 |
cursor
|
string | The next_cursor or prev_cursor from meta of the previous response. |
Example request
curl "https://app.klantly.com/api/v1/meta-copilot/milestones" \
-H "Authorization: Bearer $KLANTLY_API_KEY"$client = new \GuzzleHttp\Client([
'base_uri' => 'https://app.klantly.com/api/v1/',
'headers' => ['Authorization' => 'Bearer ' . getenv('KLANTLY_API_KEY')],
]);
$response = $client->request('GET', 'meta-copilot/milestones');
$data = json_decode((string) $response->getBody(), true)['data'];const response = await fetch('https://app.klantly.com/api/v1/meta-copilot/milestones', {
headers: {
Authorization: `Bearer ${process.env.KLANTLY_API_KEY}`,
},
});
const { data } = await response.json();import os
import requests
response = requests.get(
"https://app.klantly.com/api/v1/meta-copilot/milestones",
headers={
"Authorization": f"Bearer {os.environ['KLANTLY_API_KEY']}",
},
)
data = response.json()["data"]Response 200
The response is a list with cursor pagination: data contains the objects, meta the pagination.
{
"data": [
{
"object": "meta_copilot_milestone",
"id": "18",
"kind": "leads",
"threshold": 100,
"period": "",
"value": "104.00",
"title": "De 100e lead uit Meta",
"detail": "Sinds het begin van de meting kwamen er 104 leads uit je Meta-advertenties.",
"reached_on": "2026-10-22",
"created_at": "2026-09-14T10:15:00Z"
}
],
"meta": {
"limit": 50,
"next_cursor": "eyJpZCI6IjlkM2Y2YzFlIn0",
"prev_cursor": null
}
}Possible errors
-
403
insufficient_scope— This API key has no access to this action. -
404
not_found— Not found. -
422
validation_failed— The input is invalid.
You vs. comparable businesses
/api/v1/meta-copilot/benchmark
Cost per lead, CTR, CPC and CPM of this account over 30 days against the median and best quartile of all tracked accounts with volume, anonymous and only from five accounts. position = the share of accounts that score worse.
- Scope
-
meta_copilot.read— Read the state and alerts of the Meta co-pilot - Required feature
meta_copilot
Example request
curl "https://app.klantly.com/api/v1/meta-copilot/benchmark" \
-H "Authorization: Bearer $KLANTLY_API_KEY"$client = new \GuzzleHttp\Client([
'base_uri' => 'https://app.klantly.com/api/v1/',
'headers' => ['Authorization' => 'Bearer ' . getenv('KLANTLY_API_KEY')],
]);
$response = $client->request('GET', 'meta-copilot/benchmark');
$data = json_decode((string) $response->getBody(), true)['data'];const response = await fetch('https://app.klantly.com/api/v1/meta-copilot/benchmark', {
headers: {
Authorization: `Bearer ${process.env.KLANTLY_API_KEY}`,
},
});
const { data } = await response.json();import os
import requests
response = requests.get(
"https://app.klantly.com/api/v1/meta-copilot/benchmark",
headers={
"Authorization": f"Bearer {os.environ['KLANTLY_API_KEY']}",
},
)
data = response.json()["data"]Response 200
{
"data": {
"object": "meta_copilot_benchmark",
"available": true,
"accounts": 12,
"min_accounts": 5,
"period_days": 30,
"metrics": [
{
"metric": "cost_per_lead",
"own": 14.2,
"median": 19.8,
"best_quartile": 11.5,
"position": 71,
"higher_is_better": false
}
]
}
}Possible errors
-
403
insufficient_scope— This API key has no access to this action. -
404
not_found— Not found.
Get the seasonality calendar
/api/v1/meta-copilot/seasonality
How many quote requests comparable Klantly companies receive per calendar month (the AI agent's branch, otherwise the whole platform), as a factor against the two-year average. Anonymous and only above the volume floor; otherwise available=false.
- Scope
-
meta_copilot.read— Read the state and alerts of the Meta co-pilot - Required feature
meta_copilot
Example request
curl "https://app.klantly.com/api/v1/meta-copilot/seasonality" \
-H "Authorization: Bearer $KLANTLY_API_KEY"$client = new \GuzzleHttp\Client([
'base_uri' => 'https://app.klantly.com/api/v1/',
'headers' => ['Authorization' => 'Bearer ' . getenv('KLANTLY_API_KEY')],
]);
$response = $client->request('GET', 'meta-copilot/seasonality');
$data = json_decode((string) $response->getBody(), true)['data'];const response = await fetch('https://app.klantly.com/api/v1/meta-copilot/seasonality', {
headers: {
Authorization: `Bearer ${process.env.KLANTLY_API_KEY}`,
},
});
const { data } = await response.json();import os
import requests
response = requests.get(
"https://app.klantly.com/api/v1/meta-copilot/seasonality",
headers={
"Authorization": f"Bearer {os.environ['KLANTLY_API_KEY']}",
},
)
data = response.json()["data"]Response 200
{
"data": {
"object": "meta_copilot_seasonality",
"available": true,
"scope": "branch",
"label": "Veranda en overkappingen",
"companies": 23,
"quotes_per_month": 410,
"months": [
{
"month": 3,
"factor": 1.42
}
],
"current_month": 10,
"current_factor": 0.84,
"next_month": 11,
"next_factor": 0.61,
"peak_month": 3,
"low_month": 12
}
}Possible errors
-
403
insufficient_scope— This API key has no access to this action. -
404
not_found— Not found.
Respond to today's tip
/api/v1/meta-copilot/tips/{tip}/respond
accept = the co-pilot picks it up (the tip becomes an opportunity and a next step in its strategy), self = you pick it up yourself, dismiss = not relevant. The same tip then stays away for ninety days. 404 for an unknown tip key.
- Scope
-
meta_copilot.write— Halt the Meta co-pilot with the emergency stop, accept or dismiss opportunities and approve or reject proposals (configuring the mandate is only possible in the app) - Required feature
meta_copilot
Send an Idempotency-Key and a retry after a timeout will never create a duplicate record.
Path parameters
| Name | Type | Description |
|---|---|---|
tip required |
string | The tip key as shown in the briefing (for example frequency_high). |
Example request
curl -X POST "https://app.klantly.com/api/v1/meta-copilot/tips/9d3f6c1e-4b2a-4c8e-9f1a-2b3c4d5e6f70/respond" \
-H "Authorization: Bearer $KLANTLY_API_KEY" \
-H "Idempotency-Key: 6f1c2d3e-4a5b-4c6d-8e7f-9a0b1c2d3e4f"$client = new \GuzzleHttp\Client([
'base_uri' => 'https://app.klantly.com/api/v1/',
'headers' => ['Authorization' => 'Bearer ' . getenv('KLANTLY_API_KEY')],
]);
$response = $client->request('POST', 'meta-copilot/tips/9d3f6c1e-4b2a-4c8e-9f1a-2b3c4d5e6f70/respond', [
'headers' => [
'Idempotency-Key' => '6f1c2d3e-4a5b-4c6d-8e7f-9a0b1c2d3e4f',
],
]);
$data = json_decode((string) $response->getBody(), true)['data'];const response = await fetch('https://app.klantly.com/api/v1/meta-copilot/tips/9d3f6c1e-4b2a-4c8e-9f1a-2b3c4d5e6f70/respond', {
method: 'POST',
headers: {
Authorization: `Bearer ${process.env.KLANTLY_API_KEY}`,
'Idempotency-Key': '6f1c2d3e-4a5b-4c6d-8e7f-9a0b1c2d3e4f',
},
});
const { data } = await response.json();import os
import requests
response = requests.post(
"https://app.klantly.com/api/v1/meta-copilot/tips/9d3f6c1e-4b2a-4c8e-9f1a-2b3c4d5e6f70/respond",
headers={
"Authorization": f"Bearer {os.environ['KLANTLY_API_KEY']}",
"Idempotency-Key": "6f1c2d3e-4a5b-4c6d-8e7f-9a0b1c2d3e4f",
},
)
data = response.json()["data"]Response 200
{
"data": {
"object": "meta_copilot_tip_response",
"tip": "frequency_high",
"category": "audience",
"actor": "copilot",
"title": "Je doelgroep ziet elke advertentie 3,4 keer",
"vars": {
"spend": "€ 600.00"
},
"response": "accept",
"opportunity_id": "321",
"responded_at": "2026-09-14T10:15:00Z"
}
}Possible errors
-
403
insufficient_scope— This API key has no access to this action. -
404
not_found— Not found. -
409
conflict— This conflicts with the current state. -
422
validation_failed— The input is invalid. -
422
idempotency_key_reused— This Idempotency-Key was already used for a different request. -
409
idempotency_in_progress— A request with this Idempotency-Key is still in progress.
List followed competitors
/api/v1/meta-copilot/competitors
The Facebook pages the company follows (at most five), each with its ads from Meta's public Ad Library (at most 25, running and new first): headline, text, platforms, run time and EU reach. The co-pilot reads the library weekly; is_new stays for fourteen days. No cursor.
- Scope
-
meta_copilot.read— Read the state and alerts of the Meta co-pilot - Required feature
meta_copilot
Example request
curl "https://app.klantly.com/api/v1/meta-copilot/competitors" \
-H "Authorization: Bearer $KLANTLY_API_KEY"$client = new \GuzzleHttp\Client([
'base_uri' => 'https://app.klantly.com/api/v1/',
'headers' => ['Authorization' => 'Bearer ' . getenv('KLANTLY_API_KEY')],
]);
$response = $client->request('GET', 'meta-copilot/competitors');
$data = json_decode((string) $response->getBody(), true)['data'];const response = await fetch('https://app.klantly.com/api/v1/meta-copilot/competitors', {
headers: {
Authorization: `Bearer ${process.env.KLANTLY_API_KEY}`,
},
});
const { data } = await response.json();import os
import requests
response = requests.get(
"https://app.klantly.com/api/v1/meta-copilot/competitors",
headers={
"Authorization": f"Bearer {os.environ['KLANTLY_API_KEY']}",
},
)
data = response.json()["data"]Response 200
The response is a list with cursor pagination: data contains the objects, meta the pagination.
{
"data": [
{
"object": "meta_copilot_competitor",
"id": "7",
"page_id": "115115300320906",
"page_name": "Veranda Boys",
"library_url": "https://www.facebook.com/ads/library/?active_status=all&ad_type=all&view_all_page_id=115115300320906",
"ads_active": 6,
"ads_total": 14,
"last_scanned_at": null,
"ads": [],
"created_at": "2026-09-14T10:15:00Z"
}
],
"meta": {
"limit": 50,
"next_cursor": "eyJpZCI6IjlkM2Y2YzFlIn0",
"prev_cursor": null
}
}Possible errors
-
403
insufficient_scope— This API key has no access to this action. -
404
not_found— Not found.
Follow a competitor
/api/v1/meta-copilot/competitors
By the numeric Facebook page id (found in the Ad Library, or in the dashboard by searching on name). The baseline of its ads is read right away; what is already running then does not count as new. 409 conflict for a page already followed, above the maximum of five, or without an active Meta connection.
- Scope
-
meta_copilot.write— Halt the Meta co-pilot with the emergency stop, accept or dismiss opportunities and approve or reject proposals (configuring the mandate is only possible in the app) - Required feature
meta_copilot
Send an Idempotency-Key and a retry after a timeout will never create a duplicate record.
Example request
curl -X POST "https://app.klantly.com/api/v1/meta-copilot/competitors" \
-H "Authorization: Bearer $KLANTLY_API_KEY" \
-H "Idempotency-Key: 6f1c2d3e-4a5b-4c6d-8e7f-9a0b1c2d3e4f"$client = new \GuzzleHttp\Client([
'base_uri' => 'https://app.klantly.com/api/v1/',
'headers' => ['Authorization' => 'Bearer ' . getenv('KLANTLY_API_KEY')],
]);
$response = $client->request('POST', 'meta-copilot/competitors', [
'headers' => [
'Idempotency-Key' => '6f1c2d3e-4a5b-4c6d-8e7f-9a0b1c2d3e4f',
],
]);
$data = json_decode((string) $response->getBody(), true)['data'];const response = await fetch('https://app.klantly.com/api/v1/meta-copilot/competitors', {
method: 'POST',
headers: {
Authorization: `Bearer ${process.env.KLANTLY_API_KEY}`,
'Idempotency-Key': '6f1c2d3e-4a5b-4c6d-8e7f-9a0b1c2d3e4f',
},
});
const { data } = await response.json();import os
import requests
response = requests.post(
"https://app.klantly.com/api/v1/meta-copilot/competitors",
headers={
"Authorization": f"Bearer {os.environ['KLANTLY_API_KEY']}",
"Idempotency-Key": "6f1c2d3e-4a5b-4c6d-8e7f-9a0b1c2d3e4f",
},
)
data = response.json()["data"]Response 201
{
"data": {
"object": "meta_copilot_competitor",
"id": "7",
"page_id": "115115300320906",
"page_name": "Veranda Boys",
"library_url": "https://www.facebook.com/ads/library/?active_status=all&ad_type=all&view_all_page_id=115115300320906",
"ads_active": 6,
"ads_total": 14,
"last_scanned_at": null,
"ads": [],
"created_at": "2026-09-14T10:15:00Z"
}
}Possible errors
-
403
insufficient_scope— This API key has no access to this action. -
404
not_found— Not found. -
409
conflict— This conflicts with the current state. -
422
validation_failed— The input is invalid. -
422
idempotency_key_reused— This Idempotency-Key was already used for a different request. -
409
idempotency_in_progress— A request with this Idempotency-Key is still in progress.
Unfollow a competitor
/api/v1/meta-copilot/competitors/{competitor}
Removes the page and its stored ads.
- Scope
-
meta_copilot.write— Halt the Meta co-pilot with the emergency stop, accept or dismiss opportunities and approve or reject proposals (configuring the mandate is only possible in the app) - Required feature
meta_copilot
Path parameters
| Name | Type | Description |
|---|---|---|
competitor required |
string | The id of the followed competitor. |
Example request
curl -X DELETE "https://app.klantly.com/api/v1/meta-copilot/competitors/9d3f6c1e-4b2a-4c8e-9f1a-2b3c4d5e6f70" \
-H "Authorization: Bearer $KLANTLY_API_KEY"$client = new \GuzzleHttp\Client([
'base_uri' => 'https://app.klantly.com/api/v1/',
'headers' => ['Authorization' => 'Bearer ' . getenv('KLANTLY_API_KEY')],
]);
$response = $client->request('DELETE', 'meta-copilot/competitors/9d3f6c1e-4b2a-4c8e-9f1a-2b3c4d5e6f70');
$data = json_decode((string) $response->getBody(), true)['data'];const response = await fetch('https://app.klantly.com/api/v1/meta-copilot/competitors/9d3f6c1e-4b2a-4c8e-9f1a-2b3c4d5e6f70', {
method: 'DELETE',
headers: {
Authorization: `Bearer ${process.env.KLANTLY_API_KEY}`,
},
});
const { data } = await response.json();import os
import requests
response = requests.delete(
"https://app.klantly.com/api/v1/meta-copilot/competitors/9d3f6c1e-4b2a-4c8e-9f1a-2b3c4d5e6f70",
headers={
"Authorization": f"Bearer {os.environ['KLANTLY_API_KEY']}",
},
)
data = response.json()["data"]Response 200
{
"data": {
"object": "note",
"id": "9d3f7b41-2d6f-4e8c-9b3a-4f5d6e7a8b92",
"deleted": true
}
}Possible errors
-
403
insufficient_scope— This API key has no access to this action. -
404
not_found— Not found.
Make a variant on a competitor ad
/api/v1/meta-copilot/competitors/ads/{ad}/variant
The co-pilot writes a variant on your own best ad with the ANGLE of this competitor ad — not its text: no sentence is copied, your own image stays and no promise is added that your original does not carry. The result is an opportunity of kind creative_advice with the variant ready to approve (status 201). 409 conflict when the co-pilot is off, there is no own active ad with text, the same source ad already got a variant in the last sixty days, or no usable variant came out. Costs one model call.
- Scope
-
meta_copilot.write— Halt the Meta co-pilot with the emergency stop, accept or dismiss opportunities and approve or reject proposals (configuring the mandate is only possible in the app) - Required feature
meta_copilot
Send an Idempotency-Key and a retry after a timeout will never create a duplicate record.
Path parameters
| Name | Type | Description |
|---|---|---|
ad required |
string | The id of the competitor ad (from the list of followed competitors). |
Example request
curl -X POST "https://app.klantly.com/api/v1/meta-copilot/competitors/ads/9d3f6c1e-4b2a-4c8e-9f1a-2b3c4d5e6f70/variant" \
-H "Authorization: Bearer $KLANTLY_API_KEY" \
-H "Idempotency-Key: 6f1c2d3e-4a5b-4c6d-8e7f-9a0b1c2d3e4f"$client = new \GuzzleHttp\Client([
'base_uri' => 'https://app.klantly.com/api/v1/',
'headers' => ['Authorization' => 'Bearer ' . getenv('KLANTLY_API_KEY')],
]);
$response = $client->request('POST', 'meta-copilot/competitors/ads/9d3f6c1e-4b2a-4c8e-9f1a-2b3c4d5e6f70/variant', [
'headers' => [
'Idempotency-Key' => '6f1c2d3e-4a5b-4c6d-8e7f-9a0b1c2d3e4f',
],
]);
$data = json_decode((string) $response->getBody(), true)['data'];const response = await fetch('https://app.klantly.com/api/v1/meta-copilot/competitors/ads/9d3f6c1e-4b2a-4c8e-9f1a-2b3c4d5e6f70/variant', {
method: 'POST',
headers: {
Authorization: `Bearer ${process.env.KLANTLY_API_KEY}`,
'Idempotency-Key': '6f1c2d3e-4a5b-4c6d-8e7f-9a0b1c2d3e4f',
},
});
const { data } = await response.json();import os
import requests
response = requests.post(
"https://app.klantly.com/api/v1/meta-copilot/competitors/ads/9d3f6c1e-4b2a-4c8e-9f1a-2b3c4d5e6f70/variant",
headers={
"Authorization": f"Bearer {os.environ['KLANTLY_API_KEY']}",
"Idempotency-Key": "6f1c2d3e-4a5b-4c6d-8e7f-9a0b1c2d3e4f",
},
)
data = response.json()["data"]Response 201
{
"data": {
"object": "meta_copilot_opportunity",
"id": "318",
"source": "signal",
"kind": "wasted_spend",
"status": "open",
"title": "\"Retargeting winter\" gaf € 184,20 uit zonder één conversie",
"body": "In de laatste 30 dagen is er € 184,20 besteed en zijn er 212 klikken geweest, maar geen enkele conversie.",
"impact_cents": 18420,
"confidence": "high",
"reversible": true,
"actionable": true,
"reuse_post": null,
"image_id": null,
"target_level": "adset",
"target_id": "120210000000000001",
"target_name": "Retargeting winter",
"dismissed_reason": null,
"action_id": null,
"variant": {
"reference_ad_id": "120210000000000009",
"headline": "Veranda op maat, binnen 4 weken geplaatst",
"primary_text": "Langer buiten zitten zonder gedoe? Onze aluminium veranda wordt op maat gemaakt en binnen vier weken geplaatst. Vraag vandaag een vrijblijvende offerte aan.",
"description": "Op maat gemaakt",
"cta_type": "GET_QUOTE"
},
"blueprint": {
"branch": "veranda",
"city": "Nijmegen",
"radius_km": 40,
"age_min": 30,
"age_max": 65,
"daily_budget_cents": 1500,
"has_pixel": true,
"landing_url": "https://configurator.klantly.com/offerte-aanvragen/01k7x?utm_source=facebook",
"image_id": "12",
"headline": "Veranda op maat van Veranda Boys",
"primary_text": "Langer buiten zitten, ook in het najaar? Veranda Boys maakt veranda's op maat. Vraag vrijblijvend een offerte aan.",
"description": "Vrijblijvende offerte",
"cta_type": "GET_QUOTE",
"peer_accounts": 7,
"peer_median_cpl": "31.20"
},
"decided_at": null,
"expires_at": null,
"created_at": "2026-09-14T10:15:00Z",
"updated_at": "2026-09-14T10:15:00Z"
}
}Possible errors
-
403
insufficient_scope— This API key has no access to this action. -
404
not_found— Not found. -
409
conflict— This conflicts with the current state. -
422
idempotency_key_reused— This Idempotency-Key was already used for a different request. -
409
idempotency_in_progress— A request with this Idempotency-Key is still in progress.
Preview of an ad
/api/v1/meta-copilot/ads/{ad}/preview
The address of the preview as Meta shows the ad (format: MOBILE_FEED_STANDARD, INSTAGRAM_STANDARD, INSTAGRAM_STORY or DESKTOP_FEED_STANDARD; default the mobile feed). Put the address in an iframe. Only for an ad of the own ad account (otherwise 404); if Meta cannot make a preview, 409. The address is valid for a few hours.
- Scope
-
meta_copilot.read— Read the state and alerts of the Meta co-pilot - Required feature
meta_copilot
Path parameters
| Name | Type | Description |
|---|---|---|
ad required |
string | The id of the competitor ad (from the list of followed competitors). |
Example request
curl "https://app.klantly.com/api/v1/meta-copilot/ads/9d3f6c1e-4b2a-4c8e-9f1a-2b3c4d5e6f70/preview" \
-H "Authorization: Bearer $KLANTLY_API_KEY"$client = new \GuzzleHttp\Client([
'base_uri' => 'https://app.klantly.com/api/v1/',
'headers' => ['Authorization' => 'Bearer ' . getenv('KLANTLY_API_KEY')],
]);
$response = $client->request('GET', 'meta-copilot/ads/9d3f6c1e-4b2a-4c8e-9f1a-2b3c4d5e6f70/preview');
$data = json_decode((string) $response->getBody(), true)['data'];const response = await fetch('https://app.klantly.com/api/v1/meta-copilot/ads/9d3f6c1e-4b2a-4c8e-9f1a-2b3c4d5e6f70/preview', {
headers: {
Authorization: `Bearer ${process.env.KLANTLY_API_KEY}`,
},
});
const { data } = await response.json();import os
import requests
response = requests.get(
"https://app.klantly.com/api/v1/meta-copilot/ads/9d3f6c1e-4b2a-4c8e-9f1a-2b3c4d5e6f70/preview",
headers={
"Authorization": f"Bearer {os.environ['KLANTLY_API_KEY']}",
},
)
data = response.json()["data"]Response 200
{
"data": {
"object": "meta_copilot_ad_preview",
"ad_id": "120211234567890",
"format": "MOBILE_FEED_STANDARD",
"url": "https://www.facebook.com/ads/api/preview_iframe.php?d=AQ...&t=AQ..."
}
}Possible errors
-
403
insufficient_scope— This API key has no access to this action. -
404
not_found— Not found. -
409
conflict— This conflicts with the current state. -
422
validation_failed— The input is invalid.
List images in the library
/api/v1/meta-copilot/images
The images and videos the company uploaded for ad variants, newest first, each with a signed download link. The status of a video Meta is still processing is refreshed on request. No cursor.
- Scope
-
meta_copilot.read— Read the state and alerts of the Meta co-pilot - Required feature
meta_copilot
Example request
curl "https://app.klantly.com/api/v1/meta-copilot/images" \
-H "Authorization: Bearer $KLANTLY_API_KEY"$client = new \GuzzleHttp\Client([
'base_uri' => 'https://app.klantly.com/api/v1/',
'headers' => ['Authorization' => 'Bearer ' . getenv('KLANTLY_API_KEY')],
]);
$response = $client->request('GET', 'meta-copilot/images');
$data = json_decode((string) $response->getBody(), true)['data'];const response = await fetch('https://app.klantly.com/api/v1/meta-copilot/images', {
headers: {
Authorization: `Bearer ${process.env.KLANTLY_API_KEY}`,
},
});
const { data } = await response.json();import os
import requests
response = requests.get(
"https://app.klantly.com/api/v1/meta-copilot/images",
headers={
"Authorization": f"Bearer {os.environ['KLANTLY_API_KEY']}",
},
)
data = response.json()["data"]Response 200
The response is a list with cursor pagination: data contains the objects, meta the pagination.
{
"data": [
{
"object": "meta_copilot_image",
"id": "12",
"kind": "image",
"video_status": null,
"name": "Veranda antraciet avond",
"width": 1200,
"height": 1200,
"size": 348211,
"usable": true,
"download_url": "https://app.klantly.com/api/files/quote-attachment/9d3f9e56-af10-4123-b345-e6f7a8b9c0d5?expires=1789000000&signature=8f1c…",
"created_at": "2026-09-14T10:15:00Z"
}
],
"meta": {
"limit": 50,
"next_cursor": "eyJpZCI6IjlkM2Y2YzFlIn0",
"prev_cursor": null
}
}Possible errors
-
403
insufficient_scope— This API key has no access to this action. -
404
not_found— Not found.
Get the state of a new campaign
/api/v1/meta-copilot/starter
The recognised trade (from the AI agent, the quotes and the company profile), the start questions with the company's answer and the co-pilot's suggestion per question (what do you sell, in which area, where does a customer land, how much per day, which image, which page), whether everything is complete and what is still missing otherwise (blocked: no_image, no_page or no_landing). Also works on an account without campaigns.
- Scope
-
meta_copilot.read— Read the state and alerts of the Meta co-pilot - Required feature
meta_copilot
Example request
curl "https://app.klantly.com/api/v1/meta-copilot/starter" \
-H "Authorization: Bearer $KLANTLY_API_KEY"$client = new \GuzzleHttp\Client([
'base_uri' => 'https://app.klantly.com/api/v1/',
'headers' => ['Authorization' => 'Bearer ' . getenv('KLANTLY_API_KEY')],
]);
$response = $client->request('GET', 'meta-copilot/starter');
$data = json_decode((string) $response->getBody(), true)['data'];const response = await fetch('https://app.klantly.com/api/v1/meta-copilot/starter', {
headers: {
Authorization: `Bearer ${process.env.KLANTLY_API_KEY}`,
},
});
const { data } = await response.json();import os
import requests
response = requests.get(
"https://app.klantly.com/api/v1/meta-copilot/starter",
headers={
"Authorization": f"Bearer {os.environ['KLANTLY_API_KEY']}",
},
)
data = response.json()["data"]Response 200
{
"data": {
"object": "meta_copilot_starter",
"branch": "veranda",
"branch_label": "veranda's en overkappingen",
"branch_confidence": "high",
"complete": false,
"blocked": "no_image",
"has_active_campaign": false,
"questions": []
}
}Possible errors
-
403
insufficient_scope— This API key has no access to this action. -
404
not_found— Not found.
Save answers to the start questions
/api/v1/meta-copilot/starter/intake
Each field can be sent on its own; null removes an answer so the co-pilot's suggestion applies again. product is a trade from the list; landing is a key from the options (form:<id> or website) or url:https://…; daily_budget_cents between 500 and 20000 and never above the mandate's daily ceiling (otherwise 422); image_id an image from the library. Returns the updated state.
- Scope
-
meta_copilot.write— Halt the Meta co-pilot with the emergency stop, accept or dismiss opportunities and approve or reject proposals (configuring the mandate is only possible in the app) - Required feature
meta_copilot
Body (JSON)
| Field | Type | Description |
|---|---|---|
product
optional
|
string | The trade: veranda, dakkapel, aanbouw, kozijnen, zonwering, vloeren, badkamer, keuken or generic. can be empty (null) · one of: veranda, dakkapel, aanbouw, kozijnen, zonwering, vloeren, badkamer, keuken, generic |
area_city
optional
|
string | The place the service area is centred on. can be empty (null) · at most 120 characters |
area_radius_km
optional
|
integer | The radius in kilometres (5–80). can be empty (null) · from 5 to 80 |
landing
optional
|
string | Where a customer lands: a key from the options (form:<id> or website) or url:https://… for an address of your own. can be empty (null) · at most 600 characters |
daily_budget_cents
optional
|
integer | The daily budget in cents (500–20000), never above the mandate's daily ceiling. can be empty (null) · from 500 to 20000 |
image_id
optional
|
integer | An image or video from the library. can be empty (null) |
page_id
optional
|
string | The Facebook page that acts as sender. can be empty (null) · at most 40 characters |
Example request
curl -X PUT "https://app.klantly.com/api/v1/meta-copilot/starter/intake" \
-H "Authorization: Bearer $KLANTLY_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"product": "veranda",
"area_city": "Nijmegen",
"area_radius_km": 40,
"daily_budget_cents": 1500
}'$client = new \GuzzleHttp\Client([
'base_uri' => 'https://app.klantly.com/api/v1/',
'headers' => ['Authorization' => 'Bearer ' . getenv('KLANTLY_API_KEY')],
]);
$response = $client->request('PUT', 'meta-copilot/starter/intake', [
'json' => [
'product' => 'veranda',
'area_city' => 'Nijmegen',
'area_radius_km' => 40,
'daily_budget_cents' => 1500,
],
]);
$data = json_decode((string) $response->getBody(), true)['data'];const response = await fetch('https://app.klantly.com/api/v1/meta-copilot/starter/intake', {
method: 'PUT',
headers: {
Authorization: `Bearer ${process.env.KLANTLY_API_KEY}`,
'Content-Type': 'application/json',
},
body: JSON.stringify({
"product": "veranda",
"area_city": "Nijmegen",
"area_radius_km": 40,
"daily_budget_cents": 1500
}),
});
const { data } = await response.json();import os
import requests
response = requests.put(
"https://app.klantly.com/api/v1/meta-copilot/starter/intake",
headers={
"Authorization": f"Bearer {os.environ['KLANTLY_API_KEY']}",
},
json={
"product": "veranda",
"area_city": "Nijmegen",
"area_radius_km": 40,
"daily_budget_cents": 1500
},
)
data = response.json()["data"]Response 200
{
"data": {
"object": "meta_copilot_starter",
"branch": "veranda",
"branch_label": "veranda's en overkappingen",
"branch_confidence": "high",
"complete": false,
"blocked": "no_image",
"has_active_campaign": false,
"questions": []
}
}Possible errors
-
403
insufficient_scope— This API key has no access to this action. -
422
validation_failed— The input is invalid. -
422
unknown_field— The input contains an unknown field. -
415
unsupported_media_type— This format is not supported. -
413
payload_too_large— The request body is too large. -
404
not_found— Not found. -
409
conflict— This conflicts with the current state.
Propose a new campaign
/api/v1/meta-copilot/starter/propose
The co-pilot builds the plan for a complete campaign (campaign, ad set and ad) from the answers, the setup that gave similar companies in the same trade the cheapest requests (otherwise the trade's playbook) and ad copy from the model, and files it as an opportunity of kind campaign_blueprint (status 201, with blueprint). Accepting that opportunity is the approval: everything is created paused and only then set live. Never autonomous. 409 if the questions are incomplete, there is no image or page, or the co-pilot is off. Costs one model call.
- Scope
-
meta_copilot.write— Halt the Meta co-pilot with the emergency stop, accept or dismiss opportunities and approve or reject proposals (configuring the mandate is only possible in the app) - Required feature
meta_copilot
Send an Idempotency-Key and a retry after a timeout will never create a duplicate record.
Example request
curl -X POST "https://app.klantly.com/api/v1/meta-copilot/starter/propose" \
-H "Authorization: Bearer $KLANTLY_API_KEY" \
-H "Idempotency-Key: 6f1c2d3e-4a5b-4c6d-8e7f-9a0b1c2d3e4f"$client = new \GuzzleHttp\Client([
'base_uri' => 'https://app.klantly.com/api/v1/',
'headers' => ['Authorization' => 'Bearer ' . getenv('KLANTLY_API_KEY')],
]);
$response = $client->request('POST', 'meta-copilot/starter/propose', [
'headers' => [
'Idempotency-Key' => '6f1c2d3e-4a5b-4c6d-8e7f-9a0b1c2d3e4f',
],
]);
$data = json_decode((string) $response->getBody(), true)['data'];const response = await fetch('https://app.klantly.com/api/v1/meta-copilot/starter/propose', {
method: 'POST',
headers: {
Authorization: `Bearer ${process.env.KLANTLY_API_KEY}`,
'Idempotency-Key': '6f1c2d3e-4a5b-4c6d-8e7f-9a0b1c2d3e4f',
},
});
const { data } = await response.json();import os
import requests
response = requests.post(
"https://app.klantly.com/api/v1/meta-copilot/starter/propose",
headers={
"Authorization": f"Bearer {os.environ['KLANTLY_API_KEY']}",
"Idempotency-Key": "6f1c2d3e-4a5b-4c6d-8e7f-9a0b1c2d3e4f",
},
)
data = response.json()["data"]Response 201
{
"data": {
"object": "meta_copilot_opportunity",
"id": "318",
"source": "signal",
"kind": "wasted_spend",
"status": "open",
"title": "\"Retargeting winter\" gaf € 184,20 uit zonder één conversie",
"body": "In de laatste 30 dagen is er € 184,20 besteed en zijn er 212 klikken geweest, maar geen enkele conversie.",
"impact_cents": 18420,
"confidence": "high",
"reversible": true,
"actionable": true,
"reuse_post": null,
"image_id": null,
"target_level": "adset",
"target_id": "120210000000000001",
"target_name": "Retargeting winter",
"dismissed_reason": null,
"action_id": null,
"variant": {
"reference_ad_id": "120210000000000009",
"headline": "Veranda op maat, binnen 4 weken geplaatst",
"primary_text": "Langer buiten zitten zonder gedoe? Onze aluminium veranda wordt op maat gemaakt en binnen vier weken geplaatst. Vraag vandaag een vrijblijvende offerte aan.",
"description": "Op maat gemaakt",
"cta_type": "GET_QUOTE"
},
"blueprint": {
"branch": "veranda",
"city": "Nijmegen",
"radius_km": 40,
"age_min": 30,
"age_max": 65,
"daily_budget_cents": 1500,
"has_pixel": true,
"landing_url": "https://configurator.klantly.com/offerte-aanvragen/01k7x?utm_source=facebook",
"image_id": "12",
"headline": "Veranda op maat van Veranda Boys",
"primary_text": "Langer buiten zitten, ook in het najaar? Veranda Boys maakt veranda's op maat. Vraag vrijblijvend een offerte aan.",
"description": "Vrijblijvende offerte",
"cta_type": "GET_QUOTE",
"peer_accounts": 7,
"peer_median_cpl": "31.20"
},
"decided_at": null,
"expires_at": null,
"created_at": "2026-09-14T10:15:00Z",
"updated_at": "2026-09-14T10:15:00Z"
}
}Possible errors
-
403
insufficient_scope— This API key has no access to this action. -
404
not_found— Not found. -
409
conflict— This conflicts with the current state. -
422
idempotency_key_reused— This Idempotency-Key was already used for a different request. -
409
idempotency_in_progress— A request with this Idempotency-Key is still in progress.
Upload an image
/api/v1/meta-copilot/images
Adds an image or video to the co-pilot library and registers it at Meta (adimages or advideos) in the same call. Send it as multipart/form-data with the field file: an image (jpg, png or webp, at least 600 × 600 pixels, up to 10 MB) or a video (mp4 or mov, up to 30 MB); at most 30 per account. A variant (proposal or opportunity with create_ad_variant) can then carry it via image_id on approve or accept; a video only once Meta has processed it (video_status ready). 409 conflict when the image is too small or the video too large, the maximum is reached, Meta refuses it or there is no active Meta connection.
- Scope
-
meta_copilot.write— Halt the Meta co-pilot with the emergency stop, accept or dismiss opportunities and approve or reject proposals (configuring the mandate is only possible in the app) - Required feature
meta_copilot
Body (JSON)
| Field | Type | Description |
|---|---|---|
file
required
|
string (binary) | The image or video itself, as multipart/form-data. Image: jpg, png or webp, at least 600 × 600 pixels, at most 10 MB. Video: mp4 or mov, at most 30 MB; Meta keeps processing it for a moment (video_status). |
name
optional
|
string | The name (file name without extension, or the given name). can be empty (null) · at most 255 characters |
Example request
curl -X POST "https://app.klantly.com/api/v1/meta-copilot/images" \
-H "Authorization: Bearer $KLANTLY_API_KEY"$client = new \GuzzleHttp\Client([
'base_uri' => 'https://app.klantly.com/api/v1/',
'headers' => ['Authorization' => 'Bearer ' . getenv('KLANTLY_API_KEY')],
]);
$response = $client->request('POST', 'meta-copilot/images');
$data = json_decode((string) $response->getBody(), true)['data'];const response = await fetch('https://app.klantly.com/api/v1/meta-copilot/images', {
method: 'POST',
headers: {
Authorization: `Bearer ${process.env.KLANTLY_API_KEY}`,
},
});
const { data } = await response.json();import os
import requests
response = requests.post(
"https://app.klantly.com/api/v1/meta-copilot/images",
headers={
"Authorization": f"Bearer {os.environ['KLANTLY_API_KEY']}",
},
)
data = response.json()["data"]Response 201
{
"data": {
"object": "meta_copilot_image",
"id": "12",
"kind": "image",
"video_status": null,
"name": "Veranda antraciet avond",
"width": 1200,
"height": 1200,
"size": 348211,
"usable": true,
"download_url": "https://app.klantly.com/api/files/quote-attachment/9d3f9e56-af10-4123-b345-e6f7a8b9c0d5?expires=1789000000&signature=8f1c…",
"created_at": "2026-09-14T10:15:00Z"
}
}Possible errors
-
403
insufficient_scope— This API key has no access to this action. -
422
validation_failed— The input is invalid. -
422
unknown_field— The input contains an unknown field. -
415
unsupported_media_type— This format is not supported. -
413
payload_too_large— The request body is too large. -
404
not_found— Not found. -
409
conflict— This conflicts with the current state. -
413
payload_too_large— The request body is too large. -
415
unsupported_media_type— This format is not supported.
Delete an image
/api/v1/meta-copilot/images/{image}
Removes the image from the library; an ad already created keeps its image at Meta.
- Scope
-
meta_copilot.write— Halt the Meta co-pilot with the emergency stop, accept or dismiss opportunities and approve or reject proposals (configuring the mandate is only possible in the app) - Required feature
meta_copilot
Path parameters
| Name | Type | Description |
|---|---|---|
image required |
string | The id of the image in the library. |
Example request
curl -X DELETE "https://app.klantly.com/api/v1/meta-copilot/images/9d3f6c1e-4b2a-4c8e-9f1a-2b3c4d5e6f70" \
-H "Authorization: Bearer $KLANTLY_API_KEY"$client = new \GuzzleHttp\Client([
'base_uri' => 'https://app.klantly.com/api/v1/',
'headers' => ['Authorization' => 'Bearer ' . getenv('KLANTLY_API_KEY')],
]);
$response = $client->request('DELETE', 'meta-copilot/images/9d3f6c1e-4b2a-4c8e-9f1a-2b3c4d5e6f70');
$data = json_decode((string) $response->getBody(), true)['data'];const response = await fetch('https://app.klantly.com/api/v1/meta-copilot/images/9d3f6c1e-4b2a-4c8e-9f1a-2b3c4d5e6f70', {
method: 'DELETE',
headers: {
Authorization: `Bearer ${process.env.KLANTLY_API_KEY}`,
},
});
const { data } = await response.json();import os
import requests
response = requests.delete(
"https://app.klantly.com/api/v1/meta-copilot/images/9d3f6c1e-4b2a-4c8e-9f1a-2b3c4d5e6f70",
headers={
"Authorization": f"Bearer {os.environ['KLANTLY_API_KEY']}",
},
)
data = response.json()["data"]Response 200
{
"data": {
"object": "note",
"id": "9d3f7b41-2d6f-4e8c-9b3a-4f5d6e7a8b92",
"deleted": true
}
}Possible errors
-
403
insufficient_scope— This API key has no access to this action. -
404
not_found— Not found.
The object
All fields are always present; a field without a value is null.
| Field | Type | Description |
|---|---|---|
object |
string | Always "meta_copilot". |
ad_account_id |
string | The Meta ad account, in act_ form. |
ad_account_name |
string | The name of the ad account at Meta. can be empty (null) |
currency |
string | The currency of the account. |
mode |
string | The mode: off, shadow (watching, changing nothing), copilot (everything for approval) or autopilot (applies within the mandate). one of: off, shadow, copilot, autopilot |
profile |
string | The mandate profile: careful, standard or broad. can be empty (null) · one of: careful, standard, broad |
goal_metric |
string | What the co-pilot optimises for. can be empty (null) · one of: cost_per_quote, cost_per_lead, cost_per_conversion, roas |
goal_value |
string | The target value, as text with two decimals. can be empty (null) |
monthly_budget |
string | The monthly ad budget agreement. can be empty (null) |
autonomous_classes |
array<string> | The action classes the co-pilot may apply on its own; empty in shadow mode. |
open_alerts |
integer | Number of open alerts. |
pending_proposals |
integer | Number of proposals awaiting a decision. |
enabled_at |
string (date-time) | When the co-pilot was switched on. can be empty (null) |
tier |
string | The subscription tier: copilot (shadow and co-pilot, ad budget up to € 2,500 per month), autopilot (also autopilot, up to € 7,500) or plus (unlimited, daily operational round); null without the co-pilot. can be empty (null) · one of: copilot, autopilot, plus |
ad_budget_ceiling |
string | The monthly ad budget up to which the co-pilot shifts budget on this tier, as a string with two decimals; null = unlimited. can be empty (null) |
on_expiry |
string | What happens to a proposal not decided on in time: lapse (expires, the default) or apply (applied after the deadline anyway, within every fixed limit). one of: lapse, apply |
stopped_at |
string (date-time) | When the co-pilot was last stopped. can be empty (null) |
connection_active |
boolean | Whether the Meta connection works. |
running |
object | Whether the co-pilot is busy right now: null when nothing is running. can be empty (null) |
running.state |
string | queued (the round was requested and is waiting its turn) or running (the round is in progress). one of: queued, running |
running.since |
string (date-time) | Since when. can be empty (null) |
running.kind |
string | Kind of round: operational or strategy; null while it is still waiting. can be empty (null) · one of: operational, strategy |