Ads

The Ads app keeps the shop's side of Meta, Google Ads and LinkedIn. It holds ad accounts, audience uploads, conversions sent back to the platforms, daily ad spend and product feed references, with the routes behind them.

This page is the API reference for developers reading or writing ad accounts, audience syncs, conversions, ad spend or feed references.

The Ads app connects a shop to Meta, Google Ads and LinkedIn without being an ad manager. Ads are still created on the platforms. The app holds one ad account per platform account, each backed by an Integration Studio connection. It uploads audiences of the audiences app as SHA-256 hashes of member e-mail addresses, by default only for members with a confirmed consent. It records orders from an ad click as conversions and sends them only when the visitor allowed the consent purpose of the rule. It keeps the daily spend per ad campaign, and the return on ad spend is computed when it is read. The app never calls a platform itself: the connector workflows do that.

Objects

ObjectWhat it holds
Ad accountOne account on a platform (meta, google_ads or linkedin): external_account_id, name, connection_ref, currency, and status (connected, error or disconnected). The totals of spend, clicks and conversions are kept on it (see the figures below).
Audience syncOne audience of the audiences app uploaded to one ad account: audience_id, subscription_type, active, and the counts of members, matched, uploaded and skipped members.
Ad campaign linkThe mapping of one campaign on an ad platform (external_campaign_id) to one campaign of the campaigns app (campaign_id).
Conversion ruleWhich event (order.placed, subscription.confirmed or form.submitted) is sent to which ad account, with which value source and consent purpose.
ConversionOne order and one rule: the click ID, the value, the state (ready, sent, skipped_no_consent, failed) and the attempts.
Ad spendThe spend of one ad campaign on one day in one account: spend_date, spend (the amount), currency, impressions, clicks and booking_state.
Feed referenceWhich product feed of a market is handed to which destination (google_merchant_center, meta_catalog, linkedin or other). The feed itself is built elsewhere on the platform; this app only names it by feed_id or feed_url.

Base path: https://api.revenexx.com/v1/ads.

Scopes

A key calls these routes with the capability key of each route. The App's manifest names one per operation, in the form ads.<group>.<operation>, and the API keys page explains that a key holds capability keys or a namespace such as ads.*. The scope names are listed in the tables below. A call that needs a scope the key does not hold is refused with 403.

Ad accounts

OperationScopeWhat it does
GET /v1/ads/accountsads.accounts.listLists the ad accounts. The trash is left out unless trashed asks for it.
POST /v1/ads/accountsads.accounts.createAdds an ad account.
GET /v1/ads/accounts/{id}ads.accounts.getReads one ad account.
PUT /v1/ads/accounts/{id}ads.accounts.updateRenames an ad account or changes its connection.
DELETE /v1/ads/accounts/{id}ads.accounts.deleteMoves an ad account into the trash. A second DELETE on the same account deletes it for good.
POST /v1/ads/accounts/{id}/restoreads.accounts.restoreTakes an ad account back out of the trash.
POST /v1/ads/accounts/trash/emptyads.accounts.empty-trashDeletes every ad account in the trash for good.
POST /v1/ads/accounts/trash/purgeads.accounts.purgeDeletes the ad accounts that have been in the trash longer than the tenant's trash retention. It is the nightly purge, callable by hand.
POST /v1/ads/accounts/{id}/statusads.accounts.statusReports the state of an ad account's connection. A connector workflow calls it when a login expires or works again.
  • GET /v1/ads/accounts filters: platform, status (for example status=connected), currency, name, connection_ref, external_account_id, clicks_total, conversions_total, trashed (exclude, only or include), and further plain columns. limit takes 1 to 200 (default 50), with offset and order.
  • POST /v1/ads/accounts body: required platform, external_account_id and name. Optional: connection_ref and currency (three capital letters, default EUR). The app sets status itself: connected when a connection_ref is given, otherwise disconnected.
  • PUT /v1/ads/accounts/{id} body: the same fields, all optional. The platform and the account ID cannot change: 400 with account_fixed.
  • Figures on an ad account: read-only. The app recounts them; a value sent for them on a create or update is dropped.
    • spend_total (number): all spend imported for the account, in its currency (spend rows in another currency are left out). Recounted by the hourly run and after each spend import.
    • clicks_total (integer): all clicks the spend imports reported. Recounted with spend_total.
    • conversions_total (integer): conversions recorded with consent; skipped ones are not counted. Recounted with spend_total.
    • conversion_value_total (number): the value of those conversions in the account's currency. Recounted with spend_total.
    • roas (number or null): conversion_value_total divided by spend_total, two decimals; null while nothing was spent.
    • figures_at (date-time or null): when the totals were last recounted.
  • POST /v1/ads/accounts/{id}/status body: required status (connected, error or disconnected). Optional error.
  • Trash: an account moves into the trash only when no spend was ever imported for it and it never recorded a conversion. Its audience syncs, conversion rules and campaign links stay as they are. While the account is in the trash, its conversion rules convert nothing and its spend is not booked into a campaign budget.
  • Emptying the trash: POST /v1/ads/accounts/trash/empty and POST /v1/ads/accounts/trash/purge are refused for a storefront contact with 403 purge_not_allowed; only the back office may run them.
  • Trash and sync fields: the trash fields deleted_at, deleted_by, deleted_by_id, trash_batch, purge_at and purge_blocked_reason are on the ad account only; campaign links have no trash. Ad accounts and campaign links both carry the sync fields external_refs, source_data and source_synced_at, writable on create and update (an account's own platform ID stays external_account_id). Shared rules: Trash; Sync fields.
  • Errors: 400 for an unknown platform, a missing external_account_id or name, a bad currency, or an unknown status. 404 when no account has the ID. 409 with account_exists when the same platform account is added again, or in_trash when it is in the trash (restore it). 409 with trash_blocked (details names the blocker has_history) when the account cannot go into the trash. 409 with in_trash when an account in the trash is changed. 409 with not_in_trash on a restore of an account that is not in the trash.

Audience syncs

OperationScopeWhat it does
GET /v1/ads/audience-syncsads.syncs.listLists the audience syncs.
POST /v1/ads/audience-syncsads.syncs.createStarts syncing an audience to an ad account.
GET /v1/ads/audience-syncs/{id}ads.syncs.getReads one audience sync.
PUT /v1/ads/audience-syncs/{id}ads.syncs.updateSwitches a sync on or off, or changes its subscription type.
DELETE /v1/ads/audience-syncs/{id}ads.syncs.deleteStops syncing the audience.
POST /v1/ads/audience-syncs/{id}/requestads.syncs.requestRequests an upload of the audience now. It announces audience_sync.requested for the platform's workflow.
GET /v1/ads/audience-syncs/{id}/membersads.syncs.membersReads one page of the hashed members for the upload.
POST /v1/ads/audience-syncs/{id}/resultads.syncs.resultReports what an upload achieved: the matched, uploaded and skipped counts, and any error.
  • GET /v1/ads/audience-syncs filters: ad_account_id, audience_id, audience_code, subscription_type, state, active, stale and the count columns. limit takes 1 to 200 (default 50).
  • POST /v1/ads/audience-syncs body: required ad_account_id, audience_id and subscription_type. Optional: active (default true), audience_code and external_audience_id.
  • Members: each member's e-mail address is trimmed, lower-cased and hashed with SHA-256. Only members with a confirmed subscription of the sync's type are served. By default only members with consent are served, not members under the existing-customer exemption (the tenant setting audience_sync_consent_only, default on). Members without it are counted as skipped and never served. limit takes 1 to 500 (default and maximum 500).
  • Errors: 400 with subscription_type_required when no subscription type is sent. A plain 400, with no code, when ad_account_id names no ad account, and when audience_id is not a UUID. 404 when there is no such sync. 409 with sync_exists when the audience is already synced to that account. 409 with account_not_connected when an upload is requested for an account whose status is not connected (error or disconnected). 409 with in_trash when a sync is created or updated for an account in the trash (restore it first). 409 with account_in_trash on the request and members routes when the account is in the trash. 409 with sync_inactive on the request and members routes when the sync is switched off. 502 on the members route with consents_unavailable or audiences_unavailable when the members cannot be read: nothing is served without a consent answer.
OperationScopeWhat it does
GET /v1/ads/campaign-linksads.links.listLists the links between ad campaigns and campaigns.
POST /v1/ads/campaign-linksads.links.createCreates a link for one ad campaign of an ad account.
GET /v1/ads/campaign-links/{id}ads.links.getReads one link.
PUT /v1/ads/campaign-links/{id}ads.links.updateMaps the ad campaign to a campaign.
DELETE /v1/ads/campaign-links/{id}ads.links.deleteRemoves a link. Refused while the link is in use.
  • GET /v1/ads/campaign-links filters: ad_account_id, external_campaign_id, external_name, campaign_id. Filtering on campaign_id returns one campaign's links. An ad campaign first seen in a spend import gets a link without a campaign_id; GET /v1/ads/spend?booking_state=unlinked lists the spend waiting for a mapping.
  • POST /v1/ads/campaign-links body: required ad_account_id and external_campaign_id. Optional: campaign_id and external_name.
  • Mapping: mapping an ad campaign to a campaign books the spend imported for it into that campaign's budget, including spend imported before the mapping.
  • Errors: 400 for a bad campaign ID, or a missing account or ad campaign ID. 404 when there is no such link. 409 with link_exists on POST when the ad campaign already has a link. 409 with link_in_use on DELETE when spend was imported for the ad campaign, even while it is still unmapped: a link that ever received spend cannot be deleted. 400 with ad_account_id naming no ad account.

Ad spend and figures

OperationScopeWhat it does
GET /v1/ads/spendads.spend.listLists the imported daily spend.
POST /v1/ads/spendads.spend.importImports the daily spend per ad campaign. It is called by the daily spend workflow.
GET /v1/ads/spend/{id}ads.spend.getReads one day of spend.
GET /v1/ads/accounts/{id}/figuresads.spend.figuresReads the spend, clicks and consented conversions of an ad account per day, and the spend per ad campaign, largest first. It reads only.
GET /v1/ads/campaigns/{campaign_id}/roasads.spend.roasReads the return on ad spend of a campaign, computed on each read: the revenue the campaigns app attributes to it, divided by the ad spend imported for the campaign (any booking state), in the campaign's currency, with the spend per platform.
  • GET /v1/ads/spend filters: ad_account_id, external_campaign_id, campaign_id, platform, spend_date, booking_state (for example booking_state=unlinked) and further plain columns. limit takes 1 to 200 (default 50).
  • POST /v1/ads/spend body: required rows, an array of 1 to 500 daily rows. Each row needs ad_account_id (the ID of an ad account), external_campaign_id, date (YYYY-MM-DD) and spend (the amount, zero or more). Optional: currency (three capital letters, default the account's currency), impressions, clicks and external_name. Each row is kept per account, ad campaign and day, and a second import of the same day replaces the earlier row. 400 with rows_required or too_many_rows when the batch is empty or has more than 500 rows.
  • Booking: spend of a mapped ad campaign is booked into the campaign's budget, summed per campaign, platform and day. Spend of an unmapped ad campaign waits with booking_state=unlinked until it is mapped; the hourly job does not book it.
  • Figures: the window runs from the first active day to the last, at most the latest 60 days. Spend is in the account's currency only.
  • ROAS: attributed revenue divided by the ad spend imported for the campaign (any booking state), in the campaign's currency. model picks the attribution model, first_touch or last_touch (the default). An unknown model is refused with 400.
  • Errors: a row that cannot be kept is named in rejected with its index and a reason (invalid_date, invalid_spend, invalid_currency, unknown_account, external_campaign_required, not_an_object or account_in_trash); the rest of the batch is kept. 404 when there is no such row. 400 on the ROAS route for a campaign ID that is not valid.

Conversion rules

OperationScopeWhat it does
GET /v1/ads/conversion-rulesads.rules.listLists the conversion rules.
POST /v1/ads/conversion-rulesads.rules.createAdds a conversion rule.
GET /v1/ads/conversion-rules/{id}ads.rules.getReads one rule.
PUT /v1/ads/conversion-rules/{id}ads.rules.updateCorrects a rule.
DELETE /v1/ads/conversion-rules/{id}ads.rules.deleteDeletes a rule that recorded no conversion.
  • GET /v1/ads/conversion-rules filters: ad_account_id, event, active=true, value_source, consent_purpose, external_conversion_ref and name.
  • POST /v1/ads/conversion-rules body: required ad_account_id, name and event (order.placed, subscription.confirmed or form.submitted). Optional: active (default true), value_source (order_net, order_gross, fixed or none; default order_net, the grand total less tax and shipping), fixed_value, consent_purpose (default: the tenant setting conversion_consent_purpose, which is marketing), external_conversion_ref and metadata.
  • Consent: a conversion is sent only when the visitor allowed the consent purpose that the rule names. Without consent the conversion is kept as skipped_no_consent, without the click ID and the hashed e-mail, and is never sent.
  • What is recorded: only order.placed records conversions today. Rules for subscription.confirmed and form.submitted are accepted but record nothing yet. An order is recorded only when its attribution carries a click_id with click_id_kind gclid, fbclid or li_fat_id that matches the rule's ad account platform. An order with any other kind, such as msclkid, is skipped.
  • Errors: 400 with ad_account_id or name missing, an unknown event or value source, or a fixed value without an amount, and when ad_account_id names no ad account. 404 when there is no such rule. 409 with rule_in_use when a rule with recorded conversions is deleted.

Conversions

OperationScopeWhat it does
GET /v1/ads/conversionsads.conversions.listLists the recorded conversions. The workflow reads state=ready.
GET /v1/ads/conversions/{id}ads.conversions.getReads one conversion.
POST /v1/ads/conversions/{id}/resultads.conversions.resultReports whether the conversion was sent, and the platform's reference or the error.
POST /v1/ads/conversions/{id}/retryads.conversions.retryPuts a failed conversion back in line for sending.
  • GET /v1/ads/conversions filters: state, rule_id, ad_account_id, order_id, platform, event, click_id_kind and further plain columns. limit takes 1 to 200 (default 50).
  • POST .../result body: optional ok (boolean), external_ref and error.
  • POST .../retry body: no fields.
  • Errors: 409 with not_sendable when a result is reported for a conversion skipped for lack of consent. 409 with not_failed when a retry is asked for a conversion that did not fail. 404 when there is no such conversion.

Feed references

OperationScopeWhat it does
GET /v1/ads/feed-refsads.feeds.listLists the feed references.
POST /v1/ads/feed-refsads.feeds.createNames the product feed of a market for a destination.
GET /v1/ads/feed-refs/{id}ads.feeds.getReads one feed reference.
PUT /v1/ads/feed-refs/{id}ads.feeds.updateChanges a feed reference.
DELETE /v1/ads/feed-refs/{id}ads.feeds.deleteRemoves a feed reference.
  • GET /v1/ads/feed-refs filters: market, destination, feed_id, feed_url, label, active and id. There is one reference per market and destination.
  • POST /v1/ads/feed-refs body: required market and destination. Also feed_id or feed_url; the request is refused without one. Optional: active, label and metadata.
  • PUT /v1/ads/feed-refs/{id}: the market and the destination cannot change.
  • Errors: 400 with feed_source_required (details names feed_id) when neither a feed ID nor a feed address is sent. 400 with feed_url_invalid, destination_invalid, market_required or destination_required, with details.field naming the field. 400 with feed_fixed when the market or the destination is changed (details.field is market). 409 with feed_exists when a second reference is added for the same market and destination. 404 when there is no such reference.

Jobs

OperationScopeWhat it does
POST /v1/ads/runads.jobs.runRuns the hourly ads job by hand. It records conversions of orders placed in the last three hours, requests the audience uploads that are due, books again the spend the campaigns app did not take on the first try, and recounts the totals of each account.
  • Body: no fields.
  • Answer: accounts_recounted (integer) is the number of accounts whose figures the run recounted (accounts in the trash are not recounted).
  • Schedule: the same job runs every hour, at minute 17.

Vocabularies

OperationScopeWhat it does
GET /v1/ads/vocabulariesads.vocabularies.listLists the closed vocabularies this app enforces.
GET /v1/ads/vocabularies/{name}ads.vocabularies.getReads one vocabulary with its values and labels. 404 when there is no such vocabulary.
Was this page helpful?