Lifecycle · Draft

Meta 2-Tier Onboarding — Dev Runbook (Errors, Graph API, Ads Readiness)

Created 14 Jun 2026·Updated 17 Jun 2026

Latest change: Rebrand dossier from Kobi to Kobico

Draft document — deep-dive spec incomplete; content will be updated before and during build. Do not treat as signed-off implementation detail. Pack overview

Audience: Engineering starting the product worker. Captures everything we hit in the Jun 2026 dev spike — errors, fixes, access quirks, pixel fields from Graph API, and what still blocks live ads.

Related: Implementation pack · ADR 0003 · meta-dev/ · specs/provisioning/


1. Ads readiness (dev smoke)

Infrastructure verified in Jun 2026 dev spike. Run audit-readiness.ps1 for a snapshot, run-2tier-phase5-verify.ps1 for spend_cap + Page linkage, probe-smoke-campaign.ps1 -Create for PAUSED campaign+adset.

Requirement Dev spike status Blocks live ads?
Child BM + system user ✅ Verified
Ad account (account_status=1 ACTIVE) ✅ Verified
Pixel created ✅ Verified
Page for ads GET /{child_bm}/client_pages + PROFILE_PLUS_ADVERTISE No — use promoted_object.page_id
spend_cap ✅ POST works — see §1b Guardrail only
Pixel firing ❌ No site install until client IT Yes (A2 gate)
Campaign create (PAUSED smoke) ✅ Verified
Production billing (PRE-2) ⬜ Deferred Yes (production)

1b. Page linkage via client_pages

When the child BM is created with shared_page_id in owned_businesses, Meta attaches the Page to the child BM as a client page.

Check Dev result
GET /{child_bm}/client_pages ✅ Page present with PROFILE_PLUS_ADVERTISE
POST /act_{id}/campaigns + ad set with promoted_object.page_id ✅ PAUSED smoke campaign created

Onboarding worker (step 6): verify client_pages contains the OAuth Page with ADVERTISE task; store meta_page_id; campaigns use promoted_object.page_id.

1c. spend_cap — dev smoke vs production

API: POST /act_{id} with spend_cap={minor_units} (TRY → kuruş, 100 minor = 1.00 TRY).

Request Stored (dev) Meaning
100 (1.00 TRY) 10000 Meta raises to account minimum (~100 TRY)
10000 10000 100.00 TRY — use as dev smoke default until billing module sends plan cap

Production: billing/plan module passes credit_sub_limit_try × 100 minor units. Dev script default: -SpendCapTryMinor 100 (attempts 1 TRY, accepts Meta floor).

./run-2tier-phase5-verify.ps1 -SpendCapTryMinor 100

2. Pixel — fetch from Graph API (do not hand-write in prod)

After POST /act_{id}/adspixels, always GET the pixel for install code and health probes.

GET /{pixel-id}?fields=id,name,code,last_fired_time,creation_time,data_use_setting,is_created_by_business,is_unavailable,owner_business

Token: child BM system user · appsecret_proof required on all calls when app setting enabled.

Alternate (list on account):

GET /act_{id}/adspixels?fields=id,name,last_fired_time,is_created_by_business

2.2 Verified dev response (Jun 2026)

Field Example value Notes
id 1654665905839965 Store on tenant_registry.meta_pixel_id
name Kobico Test Pixel 20260614-1003
code Full HTML <script>…fbq('init','1654665905839965')… Use this in implementation_pack.meta.base_pixel_snippet — Meta-generated, includes noscript
creation_time 2026-06-14T07:03:21+0000
data_use_setting advertising_and_analytics
is_created_by_business false
is_unavailable false
owner_business { id: child_bm_id, name: "Kobico Test Client BM …" } Confirms pixel owned by child BM
last_fired_time Omitted when never fired Health probe: absent = yellow; present = green

Product rule: buildImplementationPack() calls fetchPixelFromGraph() and stores code in the pack. Fallback to template only if GET fails (log + HITL).

Reference: specs/provisioning/examples/tourism-onboarding.worker.ts · Zod: implementationPack.ts

2.3 Ad account fields for readiness audit

GET /act_{id}?fields=name,account_status,currency,spend_cap,amount_spent,balance,disable_reason
GET /{child_bm}/client_pages?fields=id,name,permitted_tasks
GET /act_{id}/campaigns?fields=id,name,status&limit=5
Dev value Meaning
account_status: 1 ACTIVE
spend_cap: "0" Fix in worker — set from credit_sub_limit (minor units)
client_pages contains OAuth Page + ADVERTISE Page linkage OK
campaigns.data: [] Expected until campaign execution

Scripts: meta-dev/fetch-pixel-details.ps1, meta-dev/audit-readiness.ps1


3. Graph API payload reference (verified)

All POST requests need access_token + appsecret_proof (HMAC-SHA256 of token with app secret).

Step 1 — Child BM

POST /{parent_bm_id}/owned_businesses
Field Value
Token Client user token (OAuth)
name kobi_{tenant_slug}_meta
vertical OTHER
shared_page_id Client Page from OAuth
page_permitted_tasks ["ADVERTISE","ANALYZE"]
timezone_id 134 (Europe/Istanbul)

Step 2 — Child system user token

POST /{child_bm_id}/access_token
Field Value
Token Parent system user
app_id Kobico app ID
scope ads_management,ads_read,business_management,catalog_management (see §2b)

2b. Child system user scopes

Always request the full set at POST /{child_bm}/access_token:

ads_management,ads_read,business_management,catalog_management
Scope Purpose
ads_management Ad account, pixel, campaigns, ad sets
ads_read Insights / reporting
business_management BM asset assignment
catalog_management POST /{child_bm}/owned_product_catalogs, feeds, product sets on child-owned catalog

Catalog API use case on the app ≠ catalog_management on system user token. With Catalog API enabled in App Dashboard → Use cases, minting still returns OAuth 100 / subcode 1690239 until App Review approves catalog_management.

State Child token Catalog onboarding
Before App Review ads_management, ads_read, business_management Ecommerce SKU blocked at catalog step — complete review first
After App Review + BM verification All four scopes incl. catalog_management POST /{child_bm}/owned_product_catalogs on child BM (only path)

Scripts: meta-dev/lib/child-system-user-scopes.ps1, run-2tier-phase1.ps1, refresh-child-token-catalog.ps1, probe-child-token-scopes.ps1.

2c. Catalog App Review gate (Kobico verified BM + test catalog)

Production rule: catalog is owned by the child BM — never created on parent and shared down.

Meta App Review for the Catalog API use case typically requires:

  1. Business verification (PRE-8) on Kobico parent BM
  2. Test catalog — create and manage a sample catalog via the app on the verified BM (App Review wizard)
  3. catalog_management approved on system user tokens

Until all three are done, child token mint returns OAuth 1690239 for catalog_management and phase 3–4 scripts exit with instructions.

After approval: re-run refresh-child-token-catalog.ps1run-2tier-phase3-catalog.ps1run-2tier-phase4-feed.ps1 on a child BM under Kobico verified parent BM.

Step 3 — Ad account

POST /{child_bm_id}/adaccount
Field Dev value
Token Child system user
currency TRY
timezone_id 134
end_advertiser Page ID
media_agency / partner NONE
funding_id Omit when card on child BM

Step 4 — Spend cap

POST /act_{id}
Body: spend_cap={minor_units}

Plan credit_sub_limit_try × 100 for TRY kuruş.

Step 5 — Pixel

POST /act_{id}/adspixels
Body: name=...

Then GET pixel (§2) for code.

Step 6 — Verify Page on child BM

GET /{child_bm}/client_pages?fields=id,name,permitted_tasks

Confirm OAuth Page ID is present with PROFILE_PLUS_ADVERTISE. Script: run-2tier-phase5-verify.ps1

Step 7 — System user on ad account

POST /act_{id}/assigned_users
Body: user={child_system_user_id}&tasks=MANAGE,ADVERTISE,ANALYZE&business={child_bm_id}

Step 8 — Catalog on child BM (ecommerce / tourism + feed)

Token: child system user with catalog_management (App Review + PRE-8).

POST /{child_bm}/owned_product_catalogs
POST /{catalog_id}/product_sets
POST /{catalog_id}/product_feeds
POST /{feed_id}/uploads
POST /act_{id}/catalogs

Verify: GET /{child_bm}/owned_product_catalogs contains the catalog.

Script: run-2tier-phase3-catalog.ps1, run-2tier-phase4-feed.ps1


4. Errors we hit — and fixes

# Symptom Cause Fix
1 Graph POST returns generic error / invalid signature Missing appsecret_proof HMAC-SHA256(app_secret, access_token) on every POST and GET
2 owned_businesses fails with parent system user Meta requires client user token for child BM create OAuth in portal first; pass user token to worker step 1 only
3 Catalog step blocked — no catalog_management on child token App Review / BM verification incomplete (OAuth 1690239 at mint) Complete Catalog API review on Kobico verified BM; re-mint token; POST /{child_bm}/owned_product_catalogs
4 Ad account create fails without funding_id No payment method on BM Dev: add card on child BM; prod: PRE-2 parent LOC share
5 Child BM not visible in Business Settings UI Agency-managed 2-Tier — expected Ops: POST /{child_bm}/business_users ADMIN invite, or API-only
6 Email ADMIN invite fails User not on Meta / wrong email Fallback: assign by Meta user ID (grant-child-admin.ps1)
7 Feed URL fetch fails locally Cloudflare bot protection Meta server-side fetch succeeded; allowlist Meta crawlers for client feeds
8 product_feeds upload empty Wrong API sequence Use POST /{feed}/uploads with url=, not only scheduled feed
9 Graph GET returns nonsense object ID PowerShell $path?$qs parsed as ternary Use ${path}?${qs} in URL builders
10 last_fired_time missing on pixel Pixel never installed on website Expected pre-client-install; use for health yellow/green
11 spend_cap: 0 Not set in dev phase 2 POST /act_{id} — prod value from billing module; dev: -SpendCapTryMinor 100
12 spend_cap POST 100 stores 10000 Meta minimum for TRY (~100 TRY) Accept floor in dev; billing module sends real cap in prod

5. Access & permission issues

Topic Detail
Child token scopes Request ads_management,ads_read,business_management,catalog_management at mint. catalog_management requires App Review + PRE-8 BM verification — ecommerce catalog step blocked until granted
User token scopes Need business_management + Page scopes for OAuth / owned_businesses
Dev mode child BM limit ~2 child BMs until App Review — enough to prove flow
PRE-10 2-Tier API at production scale — Meta rep
PRE-2 Extended credit on parent BM — production billing
Human BM access Optional; business_users with role ADMIN on child BM
Client never needs BM UI, ad account ownership, platform billing

6. Code artifacts (copy to product monorepo)

Path Purpose
specs/provisioning/schemas/implementationPack.ts Zod schemas — pack, pixel Graph response, vertical template
specs/provisioning/templates/*.meta.json Vertical event templates (health, school, tourism, ecommerce)
specs/provisioning/examples/tourism-onboarding.worker.ts Full tourism example — Graph client, onboarding, buildImplementationPack, pixel GET
meta-dev/run-2tier-phase*.ps1 Verified Graph sequences (PowerShell reference)

7. Tourism — example implementation direction

Vertical: tourism (e.g. Kalkan Villa–style property listings)

Flow:

  1. Client completes portal → OAuth Page + feed URL (https://…/feed.php)
  2. Worker runs steps §3 (1–8 if catalog SKU)
  3. fetchPixelFromGraph(pixelId) → store code in pack
  4. buildImplementationPack('tourism') → Lead, ViewContent, InitiateCheckout, Purchase snippets
  5. Portal Screen 7 → client IT installs pixel + events
  6. Health job polls last_fired_time → green → A2 launch allowed
  7. Campaign execution (separate module) creates paused DPA / lead gen campaign

Primary optimize event: Lead (inquiry) or Purchase (online booking)

See full TypeScript: tourism-onboarding.worker.ts

Client install example (after pack generated):

// From implementation_pack.meta — base pixel already in <head> via pixel.code from Graph
fbq('track', 'Lead');  // inquiry form success

fbq('track', 'ViewContent', {
  content_type: 'product',
  content_ids: ['VILLA-123'],
});

fbq('track', 'Purchase', {
  value: 15000.00,
  currency: 'TRY',
  content_ids: ['VILLA-123'],
  content_type: 'product',
});