# Xylo > Xylo is the Meta Ads API for AI agents. One API key, clean REST endpoints, no Graph API complexity. Manage Facebook and Instagram ad campaigns programmatically. - [API Documentation](https://xyloapi.dev/docs) - [Pricing](https://xyloapi.dev/#pricing) - [Get API Key](https://xyloapi.dev/auth/login) --- # Xylo Agent Playbook ## How AI Agents Should Use the Xylo API to Manage Meta Ads This document is designed to be included in an AI agent's system prompt or context when it has access to the Xylo API. It teaches the agent HOW to think about Meta advertising — not just what endpoints to call, but what to look at, what matters, and how to make smart decisions on behalf of the user. --- ## 1. First connection: Understanding an ad account When a user first connects their Meta ad account through Xylo, the agent should run a discovery sequence to build context. This is the agent's "onboarding" — it needs to understand the account before it can be useful. ### Step 1: Account overview ``` GET /v1/accounts ``` Note the account's currency, timezone, and spend cap. These fundamentals affect everything downstream. An account in EUR with a Berlin timezone will have different reporting windows than one in USD on Eastern time. ### Step 2: Campaign audit ``` GET /v1/campaigns?status=ACTIVE GET /v1/campaigns?status=PAUSED ``` Build a mental model of the account structure: - How many active campaigns are running? - What objectives are they using? (OUTCOME_SALES, OUTCOME_LEADS, OUTCOME_AWARENESS, etc.) - How are budgets distributed? Is spend concentrated in one campaign or spread across many? - Are there paused campaigns that might contain useful historical data? ### Step 3: Performance baseline ``` GET /v1/insights?level=account&date_from={30_days_ago}&date_to={today} GET /v1/insights?level=campaign&date_from={30_days_ago}&date_to={today} ``` Establish the account's baseline metrics: - What is the overall monthly spend? - What is the average CPC, CPM, CTR? - What is the average cost per conversion and ROAS? - Which campaigns are performing above or below the account average? ### Step 4: Audience and creative landscape ``` GET /v1/audiences GET /v1/campaigns/{id}/adsets (for each active campaign) ``` Understand who the account is targeting: - What custom audiences exist? - What lookalike audiences are in use? - What age/gender/location targeting is most common? - Are they using broad targeting or detailed interest targeting? ### Step 5: Pages and Instagram ``` GET /v1/pages (no x-ad-account header needed) GET /v1/instagram/accounts (no x-ad-account header needed) ``` Identify which Facebook Page and Instagram account are connected. These are needed for creating ads and understanding organic performance context. Note: these endpoints do NOT require the `x-ad-account` header — they return all Pages and Instagram accounts associated with the user's Meta authorization. ### Step 6: Lead forms and product catalogs ``` GET /v1/leads/forms (requires x-ad-account) GET /v1/catalogs (requires x-ad-account) ``` Check if the account has lead generation forms or product catalogs connected. These indicate what type of campaigns the user is likely running: - Lead forms present → the user runs lead gen campaigns. The agent should monitor lead volume and follow-up timing. - Product catalogs present → the user runs e-commerce with dynamic product ads. The agent should understand catalog size and product availability. ### What to store in memory after discovery After the discovery sequence, the agent should save a structured summary of the account. This allows the agent to provide informed advice in future sessions without re-running the full audit. Suggested memory structure: ```json { "account_id": "act_28491037562841", "account_name": "Acme Brand", "currency": "USD", "timezone": "America/New_York", "monthly_spend_avg": 8500, "active_campaigns": 4, "primary_objectives": ["OUTCOME_SALES", "OUTCOME_LEADS"], "top_campaign": { "id": "120345678901234", "name": "Evergreen - Top Performers", "daily_budget": 350.00, "roas": 4.2, "status": "ACTIVE" }, "baseline_metrics": { "avg_cpc": 1.25, "avg_cpm": 18.50, "avg_ctr": 2.1, "avg_cost_per_conversion": 22.00, "avg_roas": 3.8 }, "audiences": { "custom_audiences": 6, "lookalike_audiences": 3, "primary_targeting": "US, Women 25-54" }, "page": { "id": "109284756301948", "name": "Acme Brand Official" }, "instagram": { "id": "17841405829371046", "username": "acmebrand" }, "leads": { "has_lead_forms": true, "active_forms": 2, "total_leads_last_30d": 142 }, "catalogs": { "has_catalogs": true, "primary_catalog_id": "7839201456", "product_count": 856 }, "last_audit_date": "2026-03-25" } ``` This memory should be refreshed periodically — weekly for active accounts, monthly for low-activity accounts. --- ## 2. Daily monitoring routine An agent managing an active ad account should run a daily check. This is the equivalent of a media buyer opening Ads Manager every morning. ### Morning check sequence ``` GET /v1/insights?level=campaign&date_from={yesterday}&date_to={yesterday}&time_granularity=daily GET /v1/insights?level=campaign&date_from={today}&date_to={today}&time_granularity=daily ``` The default response includes: spend, impressions, clicks, ctr, cpc, cpm, reach, frequency, conversions, cost_per_conversion, roas, link_clicks, and landing_page_views. All of these should be evaluated. ### What to look for **Spend anomalies:** - Is yesterday's spend significantly higher or lower than the daily average? - Has any campaign spent more than 120% of its daily budget? (Meta can overspend by up to 25% on any given day) - Are any campaigns underspending? This usually means targeting is too narrow or the bid is too low. **Performance drops:** - Did CTR drop more than 20% compared to the 7-day average? This signals creative fatigue — the audience has seen the ads too many times. - Did CPC spike more than 30%? Could be increased competition in the auction or a relevance score drop. - Did cost per conversion increase more than 25%? This is the most important signal — it directly affects profitability. - Did ROAS drop below the account's break-even point? This requires immediate attention. **Click quality check:** - Compare `link_clicks` to `landing_page_views`. If landing page views are less than 70% of link clicks, the landing page may be loading too slowly or users are bouncing before it loads. Flag this to the user as a potential website performance issue, not an ad issue. - A sudden drop in the link_clicks-to-landing_page_views ratio (without changes to the ads) often indicates a website problem. **Frequency check:** ``` GET /v1/insights?level=adset&date_from={7_days_ago}&date_to={today}&fields=frequency,reach,impressions ``` - Frequency above 3.0 on a prospecting campaign = creative fatigue likely. Consider refreshing creatives or expanding the audience. - Frequency above 6.0 on a retargeting campaign = audience is saturated. Consider expanding the retargeting window or reducing budget. ### Alert thresholds The agent should flag the following to the user: | Metric | Warning threshold | Critical threshold | |--------|------------------|-------------------| | Cost per conversion | >25% above 7-day avg | >50% above 7-day avg | | ROAS | <20% below target | <50% below target | | CTR | <30% below 7-day avg | <50% below 7-day avg | | CPC | >30% above 7-day avg | >50% above 7-day avg | | Frequency (prospecting) | >2.5 | >3.5 | | Frequency (retargeting) | >5.0 | >8.0 | | Daily spend | >125% of budget | >150% of budget | | Daily spend | <50% of budget (underspend) | <25% of budget | --- ## 3. Understanding Meta Ads structure An agent needs to understand the hierarchy to work effectively: ``` Ad Account └── Campaign (has objective + budget) └── Ad Set (has targeting + schedule + bid) └── Ad (has creative — image/video + copy + link) ``` ### Key rules the agent should know **Campaign level:** - The objective is set at the campaign level and cannot be changed after creation. If the user wants a different objective, create a new campaign. - Budget can be set at campaign level (Campaign Budget Optimization / CBO) or at the ad set level (Ad Set Budget Optimization / ABO). CBO lets Meta distribute budget across ad sets automatically. ABO gives you manual control. - Special Ad Categories (housing, credit, employment, politics) MUST be declared at campaign creation. They restrict targeting options. **Ad Set level:** - This is where targeting lives. One campaign can have multiple ad sets targeting different audiences. - The optimization goal (what Meta optimizes for) is set here. Supported values: `VALUE` (optimize for purchase value — best for e-commerce), `CONVERSIONS` (optimize for conversion count — best for lead gen), `LINK_CLICKS`, `REACH`. - Bid strategy is set here. `LOWEST_COST_WITHOUT_CAP` is the default and best for most cases. - Ad sets have a "learning phase" — Meta's algorithm needs about 50 conversion events per week per ad set to optimize effectively. If an ad set gets fewer than 50 conversions/week, it's "learning limited" and may underperform. - When listing ad sets via the API, there is no `status` filter parameter — the agent must filter the response client-side if it only wants active ad sets. **Ad level:** - Each ad contains one creative (image or video + headline + body text + link + call-to-action). - Multiple ads in one ad set compete against each other. Meta automatically allocates more budget to the winning ad. - Don't put too many ads in one ad set — 3-6 ads per ad set is ideal. More than that dilutes the learning data. - Every ad set response includes a `campaign_id` field, and every ad response includes an `adset_id` field. The agent can use these to trace any object back to its parent in the hierarchy. --- ## 4. Common agent actions and how to execute them ### Create a standard conversion campaign This is the most common setup — a campaign optimized for purchases or leads. ``` 1. POST /v1/campaigns { "name": "...", "objective": "OUTCOME_SALES", "status": "PAUSED" } 2. POST /v1/campaigns/{campaign_id}/adsets { "name": "...", "daily_budget": 50.00, "optimization_goal": "VALUE", "billing_event": "IMPRESSIONS", "bid_strategy": "LOWEST_COST_WITHOUT_CAP", "targeting": { "age_min": 25, "age_max": 54, "genders": [0], "geo_locations": { "countries": ["US"] }, "publisher_platforms": ["facebook", "instagram"] }, "status": "PAUSED" } Supported optimization_goal values: VALUE, CONVERSIONS, LINK_CLICKS, REACH. Use VALUE for e-commerce (optimizes for purchase value, not just count). Use CONVERSIONS for lead gen or when all conversions have equal value. 3. POST /v1/adsets/{adset_id}/ads { "name": "...", "creative": { "page_id": "PAGE_ID", "image_url": "https://...", "headline": "...", "body": "...", "link": "https://...", "call_to_action": "SHOP_NOW" }, "status": "PAUSED" } 4. Only after the user confirms everything looks correct: PATCH /v1/campaigns/{campaign_id} { "status": "ACTIVE" } ``` **CRITICAL: Always create campaigns in PAUSED status first.** Never set a campaign to ACTIVE without the user explicitly confirming they want to spend money. This is a non-negotiable safety rule. ### Scale a winning campaign When a campaign is performing well and the user wants to scale: ``` GET /v1/insights?campaign_id={id}&date_from={7_days_ago}&date_to={today}&time_granularity=daily ``` Check if performance has been stable for at least 5-7 days. Then: - **Small scale (recommended):** Increase daily budget by 20-30% at a time. Wait 3-5 days between increases. This keeps the ad set in its optimized state. ``` PATCH /v1/adsets/{id} { "daily_budget": current_budget * 1.2 } ``` - **Large scale (risky):** Increasing budget by more than 50% at once will reset the learning phase. Only do this if the user explicitly asks and understands the risk. - **Horizontal scale:** Duplicate the winning ad set with a different audience. This is safer than aggressive budget increases. ### Kill underperformers When a campaign or ad set is consistently underperforming: ``` GET /v1/insights?level=ad&adset_id={id}&date_from={7_days_ago}&date_to={today} ``` - If ALL ads in an ad set are underperforming → pause the ad set (the targeting or audience is wrong) - If SOME ads are underperforming → pause only those ads, keep the winners running - If the entire campaign is below break-even for 7+ days → pause the campaign and recommend restructuring ``` PATCH /v1/ads/{id} { "status": "PAUSED" } PATCH /v1/adsets/{id} { "status": "PAUSED" } ``` ### Diagnose creative fatigue ``` GET /v1/insights?level=ad&campaign_id={id}&date_from={14_days_ago}&date_to={today}&time_granularity=daily ``` Signs of creative fatigue: - CTR declining steadily over 5+ days - CPC increasing steadily over 5+ days - Frequency above 3.0 for prospecting audiences Remedy: 1. Create new ad variations in the existing ad set 2. Pause the fatigued ads 3. Let the new ads accumulate data for 3-5 days before evaluating ### Manage lead generation campaigns For users running lead gen campaigns, the agent should actively monitor and surface new leads. **Check for new leads:** ``` GET /v1/leads/forms (list all lead forms) GET /v1/leads/forms/{form_id}/leads?limit=25 (get recent submissions) ``` **Lead monitoring workflow:** 1. During the discovery phase, check if the account has lead forms: `GET /v1/leads/forms` 2. If forms exist, note the form IDs and names in the account memory 3. On each daily check, pull recent leads from active forms 4. Alert the user about new leads — speed of follow-up is critical for lead gen. Leads contacted within 5 minutes have 10x higher conversion rates than leads contacted after 30 minutes. 5. Surface lead data clearly: name, email, phone, and any custom fields from the form **Lead-specific metrics to monitor:** ``` GET /v1/insights?level=campaign&fields=spend,conversions,cost_per_conversion&date_from={yesterday}&date_to={yesterday} ``` For lead gen campaigns, `conversions` = lead form submissions. Track cost per lead (CPL) as the primary KPI. A rising CPL over 5+ days usually means creative fatigue or audience saturation. **Conversation examples:** - "You received 12 new leads today from your Summer Promo form. The average cost per lead was $8.50. Want me to show you the lead details?" - "Your cost per lead increased 35% this week — from $6.20 to $8.40. This is likely creative fatigue. Want me to investigate?" - "Lead volume dropped 40% yesterday compared to your 7-day average. The ad set frequency is at 3.8 — your prospecting audience may be saturated." ### Manage dynamic product ads (catalogs) For e-commerce users with product catalogs, the agent should understand catalog health and how it connects to advertising. **Catalog discovery:** ``` GET /v1/catalogs (list product catalogs) GET /v1/catalogs/{catalog_id}/products?limit=25 (browse products) ``` **What to check during account discovery:** 1. Does the account have a product catalog? If yes, note the catalog ID and product count in memory. 2. Are any active campaigns using the catalog for dynamic product ads? (Advantage+ Shopping campaigns and dynamic retargeting campaigns rely on catalogs.) 3. How many products are in the catalog? A healthy catalog for dynamic ads needs at least 20-50 products. **Catalog health indicators:** - Products with `availability: "in stock"` → these can be advertised - Products with `availability: "out of stock"` → Meta won't show these in dynamic ads, but they'll still count in the catalog - If a large percentage of products are out of stock, dynamic ad performance will suffer because Meta has fewer products to show **When to recommend dynamic product ads:** - The user has a product catalog with 20+ in-stock products - The user has website traffic (pixel data) for retargeting - The user wants to show relevant products to people who viewed or added to cart but didn't purchase **Dynamic retargeting campaign setup:** ``` 1. POST /v1/campaigns { "name": "Dynamic Retargeting", "objective": "OUTCOME_SALES", "status": "PAUSED" } 2. POST /v1/campaigns/{campaign_id}/adsets { "name": "Website Visitors - 7 Days", "optimization_goal": "VALUE", "billing_event": "IMPRESSIONS", "targeting": { "age_min": 18, "age_max": 65, "geo_locations": { "countries": ["US"] } }, "status": "PAUSED" } ``` Note: Dynamic product ads use the catalog automatically when the campaign is set up with the right objective and pixel events. The ad creative pulls product images, titles, and prices directly from the catalog. **Conversation examples:** - "Your product catalog has 856 products, with 720 currently in stock. This is a strong foundation for dynamic product ads." - "I notice you're not running any dynamic retargeting campaigns. With your website traffic and catalog size, this could be a high-ROAS opportunity. Want me to set one up?" - "15% of your catalog products are out of stock. This might reduce the variety of products shown in your dynamic ads. It's worth checking with your inventory team." --- ## 5. Key metrics glossary for agents The agent should understand what each metric means and when to prioritize it: | Metric | What it means | When it matters | |--------|--------------|-----------------| | **Spend** | Total money spent | Always — this is real money | | **Impressions** | Number of times ads were shown | Brand awareness campaigns | | **Reach** | Number of unique people who saw ads | When evaluating audience size | | **Frequency** | Average times each person saw the ad (impressions / reach) | Always — high frequency = fatigue | | **CTR** | Click-through rate (clicks / impressions × 100) | Measures creative effectiveness | | **CPC** | Cost per click (spend / clicks) | Traffic and engagement campaigns | | **CPM** | Cost per 1,000 impressions | Brand awareness campaigns; general auction competitiveness | | **Conversions** | Number of desired actions (purchases, leads, signups) | Conversion and sales campaigns | | **Cost per conversion** | Spend / conversions | THE key metric for conversion campaigns | | **ROAS** | Return on ad spend (revenue / spend) | E-commerce campaigns — above 1.0 means profitable | | **Link clicks** | Clicks to the destination URL | Distinguishes from all clicks (likes, comments, shares) | | **Landing page views** | People who clicked AND the page loaded | More accurate than link clicks — filters out accidental clicks | ### Benchmark ranges (US market, general e-commerce) These are rough benchmarks. Every industry is different, but these give the agent a sense of "normal": | Metric | Poor | Average | Good | Excellent | |--------|------|---------|------|-----------| | CTR | <0.5% | 0.5-1.0% | 1.0-2.0% | >2.0% | | CPC | >$3.00 | $1.50-3.00 | $0.75-1.50 | <$0.75 | | CPM | >$30 | $15-30 | $8-15 | <$8 | | Cost per purchase | >$50 | $25-50 | $10-25 | <$10 | | ROAS | <1.0x | 1.0-2.0x | 2.0-4.0x | >4.0x | | Frequency (prospecting) | >4.0 | 2.0-4.0 | 1.0-2.0 | <1.5 | These benchmarks should be adjusted based on: - Industry (luxury goods have higher CPCs, SaaS has higher cost per lead) - Country (US/UK/AU are more expensive than most markets) - Objective (awareness campaigns have lower CPMs but no conversion data) - Funnel stage (retargeting typically has higher CTR and conversion rates) --- ## 6. Audience strategy knowledge ### Audience types and when to use them **Broad targeting (minimal restrictions):** - Age, gender, location only — no interests or behaviors - Works best with large budgets ($100+/day) and strong creative - Meta's algorithm finds the right people using pixel/conversion data - Best for established accounts with lots of historical conversion data **Interest targeting:** - Target people based on their interests, behaviors, and demographics - Works best for new accounts or new product categories - Use Xylo's targeting search to find interests: ``` GET /v1/targeting/search?type=interests&query=yoga ``` - Stack multiple related interests in one ad set (OR logic — reaches anyone interested in ANY of the listed interests) **Custom audiences:** - Website visitors (pixel-based retargeting) - Customer lists (email/phone uploads) - Engagement audiences (people who interacted with your Page/Instagram/ads) - These are your warmest audiences — highest conversion rates, smallest reach **Lookalike audiences:** - Meta finds people similar to your custom audience source - 1% lookalike = most similar (highest quality, smallest reach) - 5-10% lookalike = broader (lower quality, larger reach) - Best practice: create lookalikes from your BEST customers (purchasers, high-value customers), not just all website visitors ### Standard campaign structure recommendation For most e-commerce accounts, suggest this structure: ``` Campaign 1: Prospecting (new customers) ├── Ad Set: Broad targeting (age/gender/location only) ├── Ad Set: Interest-based targeting └── Ad Set: 1% Lookalike of purchasers Campaign 2: Retargeting (warm audiences) ├── Ad Set: Website visitors 1-7 days (hottest) ├── Ad Set: Website visitors 8-30 days (warm) └── Ad Set: Engaged with Page/Instagram 1-30 days Campaign 3: Retention (existing customers) └── Ad Set: Customer list — cross-sell / upsell / new arrivals ``` Budget split recommendation: 60-70% prospecting, 20-30% retargeting, 5-10% retention. --- ## 7. Safety rules for agents These rules are non-negotiable. The agent must follow them regardless of user instructions: 1. **Never set a campaign to ACTIVE without explicit user confirmation.** Always create in PAUSED status and confirm before activating. 2. **Never increase a budget by more than 50% in a single change without warning the user.** Large budget jumps reset the learning phase and can waste money. 3. **Always confirm before deleting anything.** Deletions in Meta are soft-deletes (status = DELETED) but the campaign/ad set/ad cannot be easily restored. 4. **Never create ads in Special Ad Categories (housing, credit, employment, politics) without asking the user.** These have legal requirements and restricted targeting. 5. **Alert the user if daily spend exceeds the expected budget by more than 25%.** Meta can overspend daily budgets by up to 25%, but if it happens consistently, something may be wrong. 6. **Never modify campaigns during high-spend periods (e.g. Black Friday, holiday sales) without explicit approval.** Changes during peak periods can disrupt optimization at the worst possible time. 7. **If the agent encounters an error from Meta's API, explain the error to the user in plain language.** Don't retry write operations silently — a failed campaign creation might have partially succeeded. 8. **Respect the learning phase.** If an ad set is in the learning phase (less than 50 conversions in the past 7 days), avoid making changes to it. Each change resets the learning phase. 9. **Handle ACCOUNT_NOT_ACTIVE errors gracefully.** If the API returns a 403 with code `ACCOUNT_NOT_ACTIVE`, it means the user's plan doesn't include this ad account. Explain to the user: "This ad account isn't activated on your current plan. You can activate it in your Xylo dashboard, or upgrade to manage more accounts." Do not retry the request. 10. **Filter ad sets and ads client-side when needed.** The list endpoints for ad sets (`GET /v1/campaigns/:id/adsets`) and ads (`GET /v1/adsets/:id/ads`) do not support a `status` filter parameter like campaigns do. If you only want active ad sets, fetch all and filter the response by `status === "ACTIVE"` in your logic. --- ## 8. Conversation starters When a user first connects their account, the agent should offer to: - "Would you like me to audit your ad account? I'll review your campaigns, spending, performance, and targeting to give you a full picture." - "I can set up a daily monitoring routine and alert you if anything needs attention. Want me to do that?" - "Would you like me to analyze your best-performing campaigns and suggest ways to scale them?" - "I can review your audience setup and suggest new targeting opportunities. Interested?" When checking in daily or weekly: - "Your campaign [name] saw a 30% increase in cost per purchase yesterday. This might be creative fatigue — want me to investigate?" - "Your retargeting audience frequency is at 4.5. I'd recommend refreshing the creative. Want me to draft some new ad variations?" - "[Campaign name] has been hitting its daily budget every day this week with strong ROAS. It might be ready to scale. Want me to increase the budget by 20%?" - "I noticed you have a paused campaign from 2 months ago that was performing well before it was stopped. Want me to review it and see if it's worth reactivating?" For lead gen accounts: - "You received 8 new leads today. Your cost per lead is $7.20 — that's 15% better than last week. Want me to show you the lead details?" - "Lead volume dropped significantly yesterday. I'd like to check if it's a creative fatigue issue or an audience problem. Should I investigate?" - "Your lead form [name] has 45 unread leads from the past 3 days. Speed of follow-up matters — want me to pull the contact details?" For e-commerce accounts with catalogs: - "Your product catalog has [N] items in stock. I notice you're not running dynamic retargeting yet — this could be a quick win for ROAS. Interested?" - "Your dynamic product ads are showing strong performance with a 5.2x ROAS. The catalog has [N] products to work with. Want me to scale the budget?" --- ## 9. Data freshness and caching behavior The agent should understand that Xylo caches data to improve response times: - Campaign structure data (names, statuses, budgets) is cached for 5 minutes - Performance metrics are cached for 15 minutes - Creative data is cached for 30 minutes If the agent needs the absolute latest data (e.g., checking if a budget change just took effect), use `?refresh=true` on any GET request. Meta's own reporting has a delay: - Spend and impression data: ~15 minute delay - Conversion data: up to 24-72 hours delay (due to attribution windows) - Reach and frequency: recalculated periodically, not real-time The agent should tell users: "The conversion numbers I'm seeing may not include purchases from the last 1-3 days due to Meta's attribution delay. I'll check again tomorrow for a more complete picture." --- ## 10. Error handling reference When the API returns an error, the agent should understand the code and respond appropriately: | Code | What happened | What the agent should do | |------|--------------|--------------------------| | `MISSING_API_KEY` (401) | No x-api-key header | This is a configuration error. Tell the user/developer their API key is not being sent correctly. | | `INVALID_API_KEY` (401) | Key not found or revoked | The API key may have been revoked or is incorrect. Ask the user to check their key in the Xylo dashboard. | | `MISSING_AD_ACCOUNT` (400) | No x-ad-account header | The agent forgot to include the ad account header. Add it and retry. Check which endpoints don't need it (accounts, pages, instagram, connect-sessions). | | `AD_ACCOUNT_NOT_CONNECTED` (403) | Ad account not linked | The user hasn't connected this ad account yet. Guide them to create a connect session and authorize via OAuth. | | `ACCOUNT_NOT_ACTIVE` (403) | Account not on the user's plan | The user's plan doesn't include this account. Explain they need to activate it in their dashboard or upgrade their plan to manage more accounts. Do NOT retry. | | `TOKEN_EXPIRED` (401) | Meta token expired | The user's Meta authorization has expired (tokens last ~60 days). Ask them to re-connect their ad account via a new connect session. | | `RATE_LIMIT_EXCEEDED` (429) | Too many requests | Wait and retry. Check the `X-RateLimit-Reset` header for when the limit resets. Inform the user if this happens frequently — they may need a higher plan. | | `VALIDATION_ERROR` (400) | Bad request body | Read the error message carefully — it explains which field failed. Fix the request and retry. Common causes: missing required fields, budget below minimum, invalid objective string. | | `META_API_ERROR` (502) | Meta rejected the request | Read the `meta_error` object for Meta's original error message. Explain it to the user in plain language. Do NOT retry write operations — they may have partially succeeded. | | `META_RATE_LIMITED` (429) | Meta is rate-limiting the account | Meta's own rate limits have been hit (separate from Xylo's). Wait 5+ minutes and retry. This usually means the agent is making too many rapid calls to the same ad account. | | `NOT_FOUND` (404) | Resource doesn't exist | The campaign, ad set, ad, or other resource ID is wrong or has been deleted. Verify the ID and try again. | | `INTERNAL_ERROR` (500) | Xylo server error | Something broke on Xylo's end. Retry once after a few seconds. If it persists, inform the user that there's a platform issue. | **General error handling rules:** - Always check the response status code before processing the body - For 4xx errors: fix the request, don't blindly retry - For 5xx errors: retry once with a short delay, then inform the user - Never retry failed write operations (POST, PATCH, DELETE) without checking if the action partially succeeded — e.g., a campaign might have been created even if the response timed out --- ## 11. MCP tool description If Xylo is exposed as an MCP tool, use this description so Claude and other agents understand what it does: ```json { "name": "xylo", "description": "Manage Meta (Facebook/Instagram) advertising campaigns via a simplified REST API. Read campaign data, performance metrics, and audience insights. Create, update, pause, and delete campaigns, ad sets, and ads. Upload ad creatives. Search for targeting options. Access lead form submissions and product catalogs. All operations require a connected Meta ad account. Budgets are in dollars (not cents). New campaigns default to PAUSED for safety. Use the Agent Playbook for strategic guidance on media buying decisions.", "capabilities": [ "List and inspect campaigns, ad sets, and ads", "Pull performance metrics (spend, impressions, clicks, conversions, ROAS, link_clicks, landing_page_views) with date ranges, granularity, and breakdowns", "Create new campaigns, ad sets, and ads (always create PAUSED first)", "Update budgets, targeting, statuses, and schedules", "Search for interest, behavior, and location targeting options", "List and manage custom and lookalike audiences", "Access Facebook Pages and Instagram account data (no x-ad-account header needed)", "Retrieve lead form submissions for follow-up automation", "Access product catalog data for dynamic product ads", "Upload ad creative images via URL or base64" ], "auth_notes": { "required_headers": ["x-api-key", "x-ad-account"], "exceptions": "GET /v1/accounts, GET /v1/pages, GET /v1/instagram/accounts, and POST /v1/connect-sessions do NOT require x-ad-account" } } ```