Skip to main content

Run Ads from Your Own App

If you sell ads management to your customers and want them to work in your own app or customer portal instead of the Ads Manager screens, your app can drive the whole flow through the API: connect the customer's Facebook ad account, pick it, look at what is running, create a pixel, build a campaign with its creatives, go live and read the results. Most calls below are in the Ads section of the API reference; the return-address and purchase calls are under Settings.

This page follows the order a customer goes through. Each step names the calls; their fields are on the API reference pages.

Before You Start​

  • One secret API key per customer. Each customer is a sub-account of your account. Create the key inside that sub-account (Settings → API / MCP → API Keys) with the Admin role (a new key is read-only by default, and most calls below write) and keep it on your server. Send it in the X-API-Key header. Every call acts on the account whose key makes it, so the customer's ad accounts, campaigns and results stay in the customer's sub-account.
  • Your server makes the calls, the customer's browser does the sign-in. A Facebook sign-in has to be approved by a person in a browser, so step 1 hands your app a link to send the customer to.
  • The address your app is on. The sign-in returns the customer to your app only if its address is registered (step 1).
  • Ads turned on. The ads calls need Social Ads, and the ad platform (Meta Ads for Facebook), turned on for the account in Labs. A call that answers 403 with "not enabled for your account" means one of them is off.

1. Connect the Customer's Facebook Account​

Register your app's address once. On your own account (every sub-account under it inherits the list), or on one sub-account, open Settings → API / MCP → API Keys and add your app under Allowed return addresses: an https address with no path, for example https://portal.example.com, up to 10 of them. Your server can set the same list with PUT https://app.biztechstack.com/api/api-keys/allowed-return-origins and a body {"origins": ["https://portal.example.com"]}.

Start the sign-in with the customer's key, naming the page of your app the customer should come back to:

GET https://app.biztechstack.com/api/integrations/ads/connect/metaads?return_to=https://portal.example.com/ads/return

The answer is one of three:

  • {"auth_url": "..."}: send the customer's browser there within 30 minutes (next paragraph).
  • {"already_connected": true, "provider_account_id": "..."}: the account already holds a Facebook login the ads connection could reuse, and nobody has to sign in. Go straight to step 2 with that provider_account_id.
  • {"removal_pending": true, "retry_after": "..."}: a login removed less than about an hour ago is still being deleted. Try again after retry_after.

After the customer approves on Facebook, the browser comes back to your return_to page with one of these in its query string:

Query stringWhat it meansWhat to do next
ads_connect=select&platform=metaads&account=<id>The sign-in worked. account is the connected login's id.Step 2 with that id.
ads_connect=finishThe sign-in worked, but the platform did not say which login.GET https://app.biztechstack.com/api/integrations/ads/connections, take the provider_account_id of the metaads row, then step 2.
ads_connect=error&error_msg=<code>The sign-in did not finish. access_denied means the customer declined. Other codes: account_not_matched, account_lookup_failed, connection_failed, or another short code from Facebook.Show the customer a message and offer to try again.

A return_to whose address is not registered, or a link older than 30 minutes, is not refused: the customer lands on the Ads Manager of the app instead of your page.

2. Pick the Ad Accounts​

List the ad accounts the login can reach:

GET https://app.biztechstack.com/api/integrations/ads/accounts/discovered?provider_account_id=<id>&platform=metaads

Each row of ad_accounts has its name, currency, timezone, business_name, and selectable with an unusable_reason when Facebook will not let it run ads. Show them to the customer ("You have these ad accounts. Are these the ones you want added?"), then connect the ones they confirm by sending those rows back as they came:

POST https://app.biztechstack.com/api/integrations/ads/accounts/select
{"provider_account_id": "<id>", "platform": "metaads", "selections": [ <the confirmed rows> ]}

The answer lists each connected ad account with its id. That id is the ad_account_id every call below takes. Several ad accounts can be connected. One Facebook login can be connected per account: to use another login, remove the first one (DELETE https://app.biztechstack.com/api/integrations/ads/connections/{provider_account_id}). The ad platform takes about an hour to finish removing it, and only then can a different login be connected; within that hour, POST https://app.biztechstack.com/api/integrations/ads/connections/{provider_account_id}/reactivate brings the removed login and its ad accounts back.

3. Review Existing Campaigns​

  • GET https://app.biztechstack.com/api/clicktracking/ad-campaigns?ad_account_id=<ad_account_id> lists the campaigns.
  • GET https://app.biztechstack.com/api/clicktracking/ad-campaigns/{campaign_id}/tree returns one campaign with its ad sets and ads.
  • GET https://app.biztechstack.com/api/clicktracking/ad-platforms/metaads/capabilities says what the platform allows (goals, placements, budgets), so your screens offer only what will work.

4. Create a Tracking Pixel​

GET https://app.biztechstack.com/api/clicktracking/ad-measurement/{ad_account_id}/pixels lists the ad account's pixels, and POST to the same address with {"name": "Main website"} creates one. Creating a pixel does not install it: the answer carries its code, which goes on the customer's website. To send conversions to the ad platform from the server as well, set up conversion forwarding with POST https://app.biztechstack.com/api/clicktracking/ad-measurement/forwarders.

5. Create a Campaign​

Build the campaign from what the customer chose, and check it before creating it:

POST https://app.biztechstack.com/api/clicktracking/ad-create/validate
{"ad_account_id": "<ad_account_id>", "spec": { ... }}

Call it whenever the customer changes something, and show its problems next to the fields they name. Then create it:

POST https://app.biztechstack.com/api/clicktracking/ad-create
{"ad_account_id": "<ad_account_id>", "spec": { ... }, "idempotency_key": "<one per screen>"}

The spec holds the whole ad, for example:

{
"name": "Spring estimates",
"goal": "traffic",
"budgetAmount": 20,
"budgetType": "daily",
"countries": ["US"],
"headline": "Book your free estimate",
"body": "Kitchen and bath remodels, planned with you.",
"linkUrl": "https://example.com/estimate",
"imageUrl": "https://example.com/kitchen.jpg",
"callToAction": "LEARN_MORE"
}
  • goal is one of engagement, traffic, awareness, video_views, lead_generation, lead_conversion, conversions, app_promotion, catalog_sales, page_likes and job_applicants, as the platform's capabilities allow.
  • A lead ad (lead_generation) opens one of the ad account's lead forms instead of a link: give its leadGenFormId. GET and POST https://app.biztechstack.com/api/clicktracking/ad-creatives/{ad_account_id}/lead-forms list and create lead forms.
  • budgetAmount is in the ad account's currency, in whole units (20 is 20 dollars on a dollar account); budgetType is daily or lifetime.
  • On Facebook the campaign is created paused unless the spec says "status": "ACTIVE", so the customer can review it first.
  • Make the idempotency_key once per create screen and send the same one if you retry: a repeated call returns the same campaign instead of a second one.

6. Make Creatives and Attach Them​

  • Add an image to the ad account from a URL or a Media Library file: POST https://app.biztechstack.com/api/clicktracking/ad-creatives/{ad_account_id}/images with {"url": "..."} or {"media_file_id": "..."}.
  • Generate one with AI: POST https://app.biztechstack.com/api/image-generation/generate.
  • Keep a creative library: GET and POST https://app.biztechstack.com/api/clicktracking/ad-creatives/{ad_account_id}/creatives list and save creatives, so your dropdown can offer them.
  • Preview how an ad will look: POST https://app.biztechstack.com/api/clicktracking/ad-creatives/{ad_account_id}/preview.

To attach a creative to a campaign the customer picked from your dropdown, create the ad (step 5) with the ids in its spec:

  • adSetId and existingCreativeId: one more ad, with that saved creative, in that ad set.
  • existingCampaignId: a new ad set, with its ad, in that campaign.

Attach by id: names are never matched.

7. Review and Go Live​

Show the customer the campaign with its ad sets and ads (/tree, step 3). When they approve, start it:

POST https://app.biztechstack.com/api/clicktracking/ad-campaigns/{campaign_id}/status
{"status": "active"}

{"status": "paused"} stops it again.

8. Show the Results and an Estimated Return​

  • GET https://app.biztechstack.com/api/clicktracking/ad-reporting/summary?entity_type=campaign&entity_id=<campaign_id>&from_date=2026-11-01&to_date=2026-11-07 sums a campaign's (or an account's, ad set's or ad's) spend, impressions, clicks, results and purchase value over the dates, with the rates: CTR, CPC, CPM, cost per result and ROAS.
  • GET https://app.biztechstack.com/api/clicktracking/ad-insights/campaigns/{campaign_id}/analytics returns the campaign's analytics with breakdowns.

Estimated return on ad spend. GET https://app.biztechstack.com/api/clicktracking/ad-reporting/roas-estimate?ad_account_id=<ad_account_id>&goal=<goal> answers a typical range, low, median and high, and always says what it is based on:

basisWhere the range comes from
own_historyThis ad account's own days with sales in the last 90 days.
platform_benchmarkAccounts on the same ad platform with the same kind of goal, over the last 90 days, when enough of them have sales.
published_rangeFigures a named publication printed (its name is in the note), not our data. Today they exist for sales goals (conversions, catalog_sales, app_promotion) on Facebook and Google Ads only.

Show the note with the numbers: it says, in a sentence your customer can read, that the range is typical and not a promise. When there is nothing to base an estimate on, the answer is 422 with the code roas_estimate_unavailable: for a lead or traffic goal on an ad account with fewer than 14 days of its own sales in the last 90 days, that is the answer today, so hide the estimate rather than show a guess.

9. Keep It Optimized​

To have the AI review a campaign on a schedule (for example every morning, with yesterday's results), create a scheduled task for the customer's account with POST https://app.biztechstack.com/api/assistant/scheduled-tasks, with instructions that say what to check and what to do.

10. Know What Each Customer Bought​

Your app can show "I see you purchased Facebook Ads Management, here are the steps" by reading what the customer bought from you:

  • With the customer's own key: GET https://app.biztechstack.com/api/account/purchases.
  • With your own account's key: GET https://app.biztechstack.com/api/sub_account/{tenant_id}/purchases, for any sub-account directly under your account (tenant_id is the sub-account's id from GET https://app.biztechstack.com/api/sub_account).

Each purchase has its product_id, product_name, kind (saas_plan, saas_addon or product), status, is_recurring and price. The purchases are those of the sub-account's owner: one person who owns several sub-accounts under your account sees the same purchases on each.

To be told the moment a customer buys, build a workflow on your own account: trigger Product purchased (or SaaS plan purchased, SaaS add-on purchased, Payment received), limited to the product, then a Webhook step that posts to your server. In the step's data rows, pick the trigger's sub_account_id, sub_account_name, customer_email and purchased_product from the variable picker: sub_account_id tells your server which customer's sub-account the purchase is for (empty when the buyer owns no sub-account under your account).

What Your Server Cannot Do​

  • Finish the Facebook sign-in by itself. A person approves it in a browser (step 1).
  • Sign a customer in to the app for them. Your app keeps its own sign-in.
  • Connect a second Facebook login on one account while the first is connected (step 2).