Klantly Developers

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 the co-pilot state

GET /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
curl "https://app.klantly.com/api/v1/meta-copilot" \
  -H "Authorization: Bearer $KLANTLY_API_KEY"
PHP
$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'];
JavaScript
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();
Python
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

Example response
{
  "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

In addition, every endpoint can return the general errors, such as an invalid key or a reached limit. See all error codes.

Update the co-pilot mandate

PATCH /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)

FieldTypeDescription
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
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"
}'
PHP
$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'];
JavaScript
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();
Python
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

Example response
{
  "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

In addition, every endpoint can return the general errors, such as an invalid key or a reached limit. See all error codes.

List co-pilot alerts

GET /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

NameTypeDescription
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
curl "https://app.klantly.com/api/v1/meta-copilot/alerts?filter[status]=open" \
  -H "Authorization: Bearer $KLANTLY_API_KEY"
PHP
$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'];
JavaScript
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();
Python
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.

Example response
{
  "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

In addition, every endpoint can return the general errors, such as an invalid key or a reached limit. See all error codes.

Execute the emergency stop

POST /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
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"
PHP
$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'];
JavaScript
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();
Python
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

Example response
{
  "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

In addition, every endpoint can return the general errors, such as an invalid key or a reached limit. See all error codes.

List co-pilot opportunities

GET /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

NameTypeDescription
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
curl "https://app.klantly.com/api/v1/meta-copilot/opportunities?filter[status]=open" \
  -H "Authorization: Bearer $KLANTLY_API_KEY"
PHP
$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'];
JavaScript
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();
Python
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.

Example response
{
  "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

In addition, every endpoint can return the general errors, such as an invalid key or a reached limit. See all error codes.

Let the co-pilot act on an opportunity

POST /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

NameTypeDescription
opportunity required string The id of the opportunity.

Example request

cURL
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"
PHP
$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'];
JavaScript
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();
Python
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

Example response
{
  "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

In addition, every endpoint can return the general errors, such as an invalid key or a reached limit. See all error codes.

Dismiss an opportunity

POST /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

NameTypeDescription
opportunity required string The id of the opportunity.

Example request

cURL
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
}'
PHP
$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'];
JavaScript
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();
Python
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

Example response
{
  "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

In addition, every endpoint can return the general errors, such as an invalid key or a reached limit. See all error codes.

List co-pilot proposals

GET /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

NameTypeDescription
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
curl "https://app.klantly.com/api/v1/meta-copilot/approvals?filter[status]=proposed" \
  -H "Authorization: Bearer $KLANTLY_API_KEY"
PHP
$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'];
JavaScript
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();
Python
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.

Example response
{
  "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

In addition, every endpoint can return the general errors, such as an invalid key or a reached limit. See all error codes.

Approve a proposal

POST /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

NameTypeDescription
approval required string The id of the proposal.

Example request

cURL
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"
PHP
$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'];
JavaScript
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();
Python
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

Example response
{
  "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

In addition, every endpoint can return the general errors, such as an invalid key or a reached limit. See all error codes.

Reject a proposal

POST /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

NameTypeDescription
approval required string The id of the proposal.

Example request

cURL
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"
PHP
$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'];
JavaScript
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();
Python
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

Example response
{
  "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

In addition, every endpoint can return the general errors, such as an invalid key or a reached limit. See all error codes.

Get the timeline

GET /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

NameTypeDescription
limit integer Number of entries, at most 100 (default 50). from 1 to 100 · default: 50

Example request

cURL
curl "https://app.klantly.com/api/v1/meta-copilot/timeline" \
  -H "Authorization: Bearer $KLANTLY_API_KEY"
PHP
$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'];
JavaScript
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();
Python
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.

Example response
{
  "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

In addition, every endpoint can return the general errors, such as an invalid key or a reached limit. See all error codes.

List weekly digests

GET /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

NameTypeDescription
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
curl "https://app.klantly.com/api/v1/meta-copilot/digests" \
  -H "Authorization: Bearer $KLANTLY_API_KEY"
PHP
$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'];
JavaScript
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();
Python
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.

Example response
{
  "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

In addition, every endpoint can return the general errors, such as an invalid key or a reached limit. See all error codes.

List morning briefings

GET /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

NameTypeDescription
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
curl "https://app.klantly.com/api/v1/meta-copilot/briefings" \
  -H "Authorization: Bearer $KLANTLY_API_KEY"
PHP
$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'];
JavaScript
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();
Python
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.

Example response
{
  "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

In addition, every endpoint can return the general errors, such as an invalid key or a reached limit. See all error codes.

Get the revenue bridge

GET /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

NameTypeDescription
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
curl "https://app.klantly.com/api/v1/meta-copilot/revenue?days=30" \
  -H "Authorization: Bearer $KLANTLY_API_KEY"
PHP
$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'];
JavaScript
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();
Python
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

Example response
{
  "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

In addition, every endpoint can return the general errors, such as an invalid key or a reached limit. See all error codes.

Get the campaign figures

GET /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

NameTypeDescription
days integer How many days back: 7, 14, 30 or 90 (default 30). one of: 7, 14, 30, 90 · default: 30

Example request

cURL
curl "https://app.klantly.com/api/v1/meta-copilot/stats?days=30" \
  -H "Authorization: Bearer $KLANTLY_API_KEY"
PHP
$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'];
JavaScript
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();
Python
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

Example response
{
  "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

In addition, every endpoint can return the general errors, such as an invalid key or a reached limit. See all error codes.

Get the co-pilot team

GET /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
curl "https://app.klantly.com/api/v1/meta-copilot/crew" \
  -H "Authorization: Bearer $KLANTLY_API_KEY"
PHP
$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'];
JavaScript
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();
Python
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

Example response
{
  "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

In addition, every endpoint can return the general errors, such as an invalid key or a reached limit. See all error codes.

Get what the co-pilot delivered

GET /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
curl "https://app.klantly.com/api/v1/meta-copilot/value" \
  -H "Authorization: Bearer $KLANTLY_API_KEY"
PHP
$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'];
JavaScript
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();
Python
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

Example response
{
  "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

In addition, every endpoint can return the general errors, such as an invalid key or a reached limit. See all error codes.

List milestones

GET /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

NameTypeDescription
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
curl "https://app.klantly.com/api/v1/meta-copilot/milestones" \
  -H "Authorization: Bearer $KLANTLY_API_KEY"
PHP
$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'];
JavaScript
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();
Python
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.

Example response
{
  "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

In addition, every endpoint can return the general errors, such as an invalid key or a reached limit. See all error codes.

You vs. comparable businesses

GET /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
curl "https://app.klantly.com/api/v1/meta-copilot/benchmark" \
  -H "Authorization: Bearer $KLANTLY_API_KEY"
PHP
$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'];
JavaScript
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();
Python
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

Example response
{
  "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

In addition, every endpoint can return the general errors, such as an invalid key or a reached limit. See all error codes.

Get the seasonality calendar

GET /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
curl "https://app.klantly.com/api/v1/meta-copilot/seasonality" \
  -H "Authorization: Bearer $KLANTLY_API_KEY"
PHP
$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'];
JavaScript
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();
Python
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

Example response
{
  "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

In addition, every endpoint can return the general errors, such as an invalid key or a reached limit. See all error codes.

Respond to today's tip

POST /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

NameTypeDescription
tip required string The tip key as shown in the briefing (for example frequency_high).

Example request

cURL
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"
PHP
$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'];
JavaScript
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();
Python
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

Example response
{
  "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

In addition, every endpoint can return the general errors, such as an invalid key or a reached limit. See all error codes.

List followed competitors

GET /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
curl "https://app.klantly.com/api/v1/meta-copilot/competitors" \
  -H "Authorization: Bearer $KLANTLY_API_KEY"
PHP
$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'];
JavaScript
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();
Python
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.

Example response
{
  "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

In addition, every endpoint can return the general errors, such as an invalid key or a reached limit. See all error codes.

Follow a competitor

POST /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
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"
PHP
$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'];
JavaScript
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();
Python
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

Example response
{
  "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

In addition, every endpoint can return the general errors, such as an invalid key or a reached limit. See all error codes.

Unfollow a competitor

DELETE /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

NameTypeDescription
competitor required string The id of the followed competitor.

Example request

cURL
curl -X DELETE "https://app.klantly.com/api/v1/meta-copilot/competitors/9d3f6c1e-4b2a-4c8e-9f1a-2b3c4d5e6f70" \
  -H "Authorization: Bearer $KLANTLY_API_KEY"
PHP
$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'];
JavaScript
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();
Python
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

Example response
{
  "data": {
    "object": "note",
    "id": "9d3f7b41-2d6f-4e8c-9b3a-4f5d6e7a8b92",
    "deleted": true
  }
}

Possible errors

In addition, every endpoint can return the general errors, such as an invalid key or a reached limit. See all error codes.

Make a variant on a competitor ad

POST /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

NameTypeDescription
ad required string The id of the competitor ad (from the list of followed competitors).

Example request

cURL
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"
PHP
$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'];
JavaScript
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();
Python
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

Example response
{
  "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

In addition, every endpoint can return the general errors, such as an invalid key or a reached limit. See all error codes.

Preview of an ad

GET /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

NameTypeDescription
ad required string The id of the competitor ad (from the list of followed competitors).

Example request

cURL
curl "https://app.klantly.com/api/v1/meta-copilot/ads/9d3f6c1e-4b2a-4c8e-9f1a-2b3c4d5e6f70/preview" \
  -H "Authorization: Bearer $KLANTLY_API_KEY"
PHP
$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'];
JavaScript
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();
Python
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

Example response
{
  "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

In addition, every endpoint can return the general errors, such as an invalid key or a reached limit. See all error codes.

List images in the library

GET /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
curl "https://app.klantly.com/api/v1/meta-copilot/images" \
  -H "Authorization: Bearer $KLANTLY_API_KEY"
PHP
$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'];
JavaScript
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();
Python
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.

Example response
{
  "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

In addition, every endpoint can return the general errors, such as an invalid key or a reached limit. See all error codes.

Get the state of a new campaign

GET /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
curl "https://app.klantly.com/api/v1/meta-copilot/starter" \
  -H "Authorization: Bearer $KLANTLY_API_KEY"
PHP
$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'];
JavaScript
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();
Python
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

Example response
{
  "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

In addition, every endpoint can return the general errors, such as an invalid key or a reached limit. See all error codes.

Save answers to the start questions

PUT /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)

FieldTypeDescription
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
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
}'
PHP
$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'];
JavaScript
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();
Python
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

Example response
{
  "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

In addition, every endpoint can return the general errors, such as an invalid key or a reached limit. See all error codes.

Propose a new campaign

POST /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
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"
PHP
$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'];
JavaScript
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();
Python
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

Example response
{
  "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

In addition, every endpoint can return the general errors, such as an invalid key or a reached limit. See all error codes.

Upload an image

POST /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)

FieldTypeDescription
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
curl -X POST "https://app.klantly.com/api/v1/meta-copilot/images" \
  -H "Authorization: Bearer $KLANTLY_API_KEY"
PHP
$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'];
JavaScript
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();
Python
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

Example response
{
  "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

In addition, every endpoint can return the general errors, such as an invalid key or a reached limit. See all error codes.

Delete an image

DELETE /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

NameTypeDescription
image required string The id of the image in the library.

Example request

cURL
curl -X DELETE "https://app.klantly.com/api/v1/meta-copilot/images/9d3f6c1e-4b2a-4c8e-9f1a-2b3c4d5e6f70" \
  -H "Authorization: Bearer $KLANTLY_API_KEY"
PHP
$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'];
JavaScript
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();
Python
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

Example response
{
  "data": {
    "object": "note",
    "id": "9d3f7b41-2d6f-4e8c-9b3a-4f5d6e7a8b92",
    "deleted": true
  }
}

Possible errors

In addition, every endpoint can return the general errors, such as an invalid key or a reached limit. See all error codes.

The object

All fields are always present; a field without a value is null.

FieldTypeDescription
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