RevenuePilot CRMDocumentation
v1.0.0 Changelog
Generated from docs/ads-publishing-production-launch.md.

Ads Publishing Production Launch

This runbook covers the staging-first launch for direct Meta/Facebook Ads and Google Ads publishing. The system creates external campaigns paused by default. Live spend starts only after approval, budget cap validation, and explicit activation.

Version Checks

  • Meta: verify META_GRAPH_API_VERSION against the official Meta Marketing API changelog immediately before staging and production rollout: https://developers.facebook.com/docs/marketing-api/
  • Google Ads: verify GOOGLE_ADS_API_VERSION against the official release and sunset tables. As of the June 2026 implementation check, v24.1 is the released default and v24.2 is listed as a June 2026 upcoming release: https://developers.google.com/google-ads/api/docs/sunset-dates
  • Keep both versions environment-configurable. Do not hard-code provider API versions in code.

Required Environment

Add the following values to staging and production:

META_CLIENT_ID=
META_CLIENT_SECRET=
META_GRAPH_API_VERSION=v25.0

GOOGLE_ADS_CLIENT_ID=
GOOGLE_ADS_CLIENT_SECRET=
GOOGLE_ADS_DEVELOPER_TOKEN=
GOOGLE_ADS_MANAGER_CUSTOMER_ID=
GOOGLE_ADS_API_VERSION=v24.1

ADS_PUBLISHING_ENABLED=true
ADS_ACTIVATION_ENABLED=true
ADS_SANDBOX_MUTATION_ENABLED=false
ADS_AUTO_PAUSE_ENABLED=false
ADS_OFFLINE_CONVERSIONS_ENABLED=false
ADS_TRACKING_CHECKS_ENABLED=true

QUEUE_CONNECTION=sync is acceptable only for local/demo. Production must run a real queue backend and worker before scheduled activation is enabled.

Provider Readiness

Meta/Facebook Ads:

  • App review completed for ads_management.
  • Existing read scopes still available: ads_read, leads_retrieval, page discovery/read permissions.
  • Connected provider account has a valid access token.
  • Page ID is valid and accessible.
  • Ad account is billing-ready and permitted for campaign, ad set, creative, and ad mutations.

Google Ads:

  • OAuth client ID and secret configured.
  • Developer token approved for the intended environment.
  • Manager customer ID configured when required.
  • Customer ID access is confirmed.
  • Search campaign, budget, ad group, keyword, and responsive search ad mutate calls are allowed.

Deployment Steps

Run these steps on staging first:

php artisan migrate --force
php artisan optimize:clear
php artisan crm:sync-ad-account-readiness
php artisan crm:sync-ad-campaigns
php artisan crm:process-ad-activations
php artisan crm:ads-reconcile-attribution
php artisan crm:ads-check-tracking
php artisan crm:ads-monitor-spend
php artisan crm:ads-health-summary

Then verify:

  • Queue worker is running for the production queue connection.
  • Scheduler cron or task scheduler is invoking Laravel schedule every minute.
  • System Health shows recent successful runs for:
  • crm:process-ad-activations
  • crm:sync-ad-campaigns
  • crm:sync-ad-account-readiness
  • crm:ads-reconcile-attribution
  • crm:ads-check-tracking
  • crm:ads-monitor-spend
  • crm:ads-retry-failed-jobs
  • crm:ads-health-summary
  • Budget caps exist for each production provider/account/currency or the default caps are acceptable.

Budget Guardrails

Default caps are:

  • Daily: PKR 50,000
  • Lifetime: PKR 500,000

Admins with manage ad campaign budgets can set provider/account/currency policy caps from the ads cockpit. Activation revalidates caps before any external status mutation.

Production kill switches:

  • ADS_PUBLISHING_ENABLED=false blocks external publish queueing.
  • ADS_ACTIVATION_ENABLED=false blocks activation requests and activation queueing.
  • ADS_SANDBOX_MUTATION_ENABLED=false blocks sandbox smoke activation/pause mutations in production.
  • ADS_AUTO_PAUSE_ENABLED=false keeps automatic pause disabled unless operations intentionally turns it on and saves auto-pause policies.
  • ADS_OFFLINE_CONVERSIONS_ENABLED=false blocks offline conversion upload until tracking readiness and provider credentials are verified.
  • ADS_TRACKING_CHECKS_ENABLED=true keeps tracking readiness checks available from commands and cockpit actions.

Read-only sync, health summary, tracking checks, and audit export remain available when publishing or activation is disabled.

Sandbox Smoke

Run a read-only/paused smoke first:

php artisan crm:ads-sandbox-smoke --provider=facebook_meta --campaign=1
php artisan crm:ads-sandbox-smoke --provider=google_ads --campaign=1

Non-production activation/pause smoke:

php artisan crm:ads-sandbox-smoke --provider=facebook_meta --campaign=1 --activate-and-pause
php artisan crm:ads-sandbox-smoke --provider=google_ads --campaign=1 --activate-and-pause

Production activation/pause smoke requires ADS_SANDBOX_MUTATION_ENABLED=true.

Attribution and Audit

Reconcile campaign attribution after provider sync:

php artisan crm:ads-reconcile-attribution --campaign=1 --explain
php artisan crm:ads-check-tracking --campaign=1
php artisan crm:ads-monitor-spend --campaign=1

Export audit evidence:

php artisan crm:ads-export-audit --campaign=1 --path=ads/audit-campaign-1.json
php artisan crm:ads-health-summary --campaign=1 --path=ads/health-campaign-1.json

Offline conversion upload stays disabled by default. After provider credentials and tracking readiness pass, enable ADS_OFFLINE_CONVERSIONS_ENABLED=true and run:

php artisan crm:ads-upload-offline-conversions --campaign=1

Retry failed provider jobs without duplicating external mappings:

php artisan crm:ads-retry-failed-jobs --campaign=1

Production Smoke Checklist

Run the full path in staging for both Meta and Google:

  1. Create a CRM campaign.
  2. Create an ad draft.
  3. Run readiness.
  4. Validate the draft.
  5. Publish paused and confirm external mappings are paused.
  6. Request activation approval.
  7. Approve activation.
  8. Activate immediately or schedule activation.
  9. Sync status and performance.
  10. Use Emergency Pause and confirm external mappings return to paused.

Fail launch if activation bypasses approval, external mappings duplicate after retry, dark mode shows broken light containers, command health is stale, or sandbox provider mutations fail.