Skip to content

Surface ChatGPT-ads per-ad-group conversions through the ads API #705

@arberx

Description

@arberx

Summary

The ads insights path surfaces impressions, clicks, and spend per campaign and ad group, but not conversions. Downstream consumers want to rank ad groups (and the themes they map to) by conversions, so a keep/stop/scale decision can be made on real outcome data, not just spend or name matching. Conversions are currently absent end to end: not requested, not stored, not served.

Today

  • packages/canonry/src/ads-sync.ts requests only {impressions,clicks,spend} per level (the CAMPAIGN_INSIGHT_FIELDS / AD_GROUP_INSIGHT_FIELDS arrays).
  • OpenAiAdsInsightRow (packages/integration-openai-ads/src/types.ts) has no conversion field; adsInsightsDaily (packages/db/src/schema.ts) has impressions, clicks, spendMicros only; the DTOs (packages/contracts/src/ads.ts: adsInsightRowDtoSchema, adsTotalsDtoSchema) and the serializers (packages/api-routes/src/ads.ts) emit no conversions.
  • bidding_type IS already surfaced on the campaign DTO. conversion_event_setting_ids is fetched on the campaign but dropped.

Proposed (additive, no new endpoint or table)

  1. Step 0, blocking: curl a live ad account with fields[]=campaign.conversions (and likely cost_per_conversion) to confirm the real field name, then capture the response into packages/integration-openai-ads/test/fixtures.ts. Per the integration's rule, do not add a field that has not been observed. If conversions are not a fields[] metric on the existing /insights surface, re-scope to the correct endpoint.
  2. Add conversions?: number (and optionally cost_per_conversion) to OpenAiAdsInsightRow.
  3. Request the field in ads-sync.ts for both campaign and ad-group levels; read it into the insight upsert.
  4. Add a conversions column to adsInsightsDaily with a new idempotent MIGRATION_VERSIONS entry in packages/db/src/migrate.ts.
  5. Add conversions to adsInsightRowDtoSchema + adsTotalsDtoSchema; serialize on GET /ads/insights and GET /ads/summary; pnpm gen to regenerate the SDK.
  6. Render conversions in the ads insights / ads summary CLI.
  7. Tests asserting the conversion totals/roll-up math (mirror the existing adsCtr / adsCpcMicros tests) plus the schema+DTO drift checks.

The app already reads GET /ads/insights?level=ad_group, so ad-group ranking is unblocked once the field lands on the row DTO.

Size

Medium. Crosses upstream type, sync, schema + migration, DTOs, routes, CLI, and tests, but all additive, no new endpoint or table. The only unknown is the upstream field name (Step 0).

Out of scope (possible follow-up)

A caller-supplied seedQueries param on POST /projects/:name/discover/run so callers can seed the fan-out with their own phrasings (e.g. existing ad context hints). Small, but optional: a consumer can merge such seeds app-side. File separately only if engine-side dedup/probe/classification of seeded hints is wanted.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions