Use case

Running many client ad accounts from one rule set

AdOps lets an agency run one rule shape across every client ad account while each client keeps its own numbers. A condition can compare a live Meta metric against a custom metric that AdOps resolves from a Google Sheet at evaluation time, so changing a client's ROAS target is a spreadsheet edit rather than a rule edit.

One rule shape applied across client ad accounts, with each client's ROAS target and monthly cap read from a Google Sheet at evaluation time.

Updated

Situation

What changes when AdOps runs the checks?

The problem this playbook was written against, and what the account looks like once the rules are live.

Before

Every client has a different ROAS target and a different monthly cap, so the same rule has to be rewritten and re-checked per account, and one edited threshold on a Friday is discovered on a Monday.

After

One rule shape is reused across accounts, per-client numbers live in a Google Sheet the account manager owns, and each ad account gets its own queued job with its own log.

Playbook

Which rules does this playbook actually use?

Each card is one rule, printed the way the AdOps rule builder prints it: conditions on top, actions below the hairline.

Every threshold below is an example. Change the numbers to fit your account before you set a rule live.

  1. Compare against the client's own target

    Live
    Applies to
    Campaigns

    When

    • Purchase ROAS Last 7 days < custom metric target_roas
    • AND Spend Last 7 days > Rp 3,000,000

    Then

    • Pause campaign
    • Add to name Append mode · [BELOW-TARGET {date}]

    Why this rule

    The target lives in a Google Sheet row the account manager maintains, so changing a client's target is a spreadsheet edit rather than a rule edit.

  2. Hold the retainer cap

    Live
    Applies to
    Campaigns

    When

    • Spend This month > custom metric monthly_cap

    Then

    • Pause campaign
    • Add to name Append mode · [CAP-REACHED]

    Why this rule

    A retainer overspend is the one mistake an agency pays for out of its own margin, and the cap is different for every client.

  3. Ceiling on any single campaign

    Live
    Applies to
    Campaigns

    When

    • Daily Budget > Rp 2,000,000

    Then

    • Set budget to Rp 2,000,000 Fixed amount

    Why this rule

    The engine re-reads the live Daily Budget from Meta before writing, so a budget raised by hand or by another tool is pulled back to the agreed ceiling.

  4. Scope one rule to one client inside a shared account

    Live
    Applies to
    Campaigns
    Scope
    Campaigns whose Campaign Name contains ACME

    When

    • Spend Last 3 days > Rp 1,500,000
    • AND Purchases Last 3 days = 0

    Then

    • Pause campaign
    • Add to name Append mode · [NO-SALES {date}]

    Why this rule

    Campaign Name is one of the three pre-filter fields, so a naming convention already in use becomes the client boundary without a list of campaign IDs.

  5. Make the campaign list the weekly report

    Live
    Applies to
    Campaigns

    When

    • Spend Last 7 days > Rp 1,000,000

    Then

    • Add to name Append mode · [ROAS {metric|purchase_roas.omni_purchase|last_7d}]

    Why this rule

    A client who opens Ads Manager sees the same 7-day number the agency is reporting, written onto the campaign itself.

The AdOps rule list with three rules selected, the select-all band replaced by a bulk bar reading 3 selected with Set live and Turn off buttons and copy, delete and more icons. Sample data
Select several rules and set them live, turn them off, duplicate or delete them in one pass — the agency case. Read the details

Key facts

What are the numbers behind this playbook?

Work distribution
One queued job per ad account Rules due for execution are dispatched by a sweep that runs every 10 seconds
Custom metric source
Google Sheets A spreadsheet id, sheet name, lookup column and value column, resolved at evaluation time
Custom metric cache
2 minutes in memory, last-access TTL Falls back to the last rows stored in MongoDB if the live fetch fails
Pre-filter fields
3 Campaign ID equals or not equals, Campaign Name contains or does not contain, Campaign Status is or is not
Meta entity fetch retries
3 total attempts, waiting 1 second then 2 seconds
Shipped rule templates
10, in 4 categories budget, status, naming, monitoring

AdOps lets an agency run one rule shape across many Meta ad accounts while each client keeps its own numbers. A condition can compare a live Meta metric against a custom metric that AdOps resolves from a Google Sheet at evaluation time, so a client’s ROAS target or monthly cap is edited in a spreadsheet rather than inside a dozen separate rules.

Why does multi-account automation break at the fifth client?

The rules are not the hard part. The thresholds are. Ten clients means ten ROAS targets, ten monthly caps and ten different opinions about when to pause, and every one of them changes at some point in the quarter.

Copying a rule per client works until the day a target moves. Then the change has to be found in the right account, applied to the right rule, and verified, and the account manager who agreed the new number is usually not the person who edits rules.

How does a custom metric hold each client’s target?

A custom metric in AdOps names a Google Sheets spreadsheet id, a sheet name, a lookup column and a value column. At evaluation time the engine resolves the metric to a number, and a condition set to compare against a metric uses that number instead of a hardcoded threshold.

The practical shape is one sheet with a row per client: client key, target ROAS, monthly cap, floor budget. Purchase ROAS over the last 7 days is less than target_roas then means something different in every account, and updating a client’s target is a cell edit that takes effect on the next evaluation.

Two limits are worth stating. Rows are cached in memory on a 2-minute last-access TTL with the last stored rows in MongoDB as a fallback, so a continuously read sheet can serve a cached snapshot. And the lookup reads the first matching row for each formula line, so the sheet should hold one row per key.

How do you keep client work separated?

Each rule belongs to an ad account, which is the first boundary. Inside an account, three pre-filters narrow a rule further: Campaign ID with equals or not equals, Campaign Name with contains or does not contain, and Campaign Status with is or is not, over the values ACTIVE and PAUSED.

Campaign Name is the one that scales. If a shared account uses a client or brand prefix, a rule pre-filtered to campaigns whose name contains that prefix picks up new campaigns automatically the day they are created.

Ad accounts also start switched off. Every account discovered from Meta is stored inactive, and nothing is evaluated until someone activates it, which is a useful property when a new client’s account is connected mid-week.

How does the work get distributed?

The dispatcher sweeps for rules whose next execution time has passed, on a 10-second cron with overlap prevention, and enqueues one job per ad account onto a Redis-backed queue. The worker consumes the job, issues a batched Meta Insights read for the metrics the conditions need, evaluates the conditions, and executes the actions.

That split has a property worth knowing when you are handing an agency’s whole book to an automation: the dispatcher only ever issues reads to the Meta Graph API. Every budget change, pause and rename happens in the downstream worker.

Entity fetches from Meta are retried up to 3 total attempts, waiting 1 second and then 2 seconds, so a single transient Graph API error does not silently skip an account.

What does the agency see afterwards?

Every run writes a batch record and one result per campaign per task. The Rule Log Detail screen shows an Executed, Not Executed or Skipped badge per campaign, each condition marked Pass or Fail beside the value it read, the parameters the action used, the execution time in seconds, and the next scheduled execution.

For client-facing reporting, the Add to name action carries live shortcodes such as {metric|purchase_roas.omni_purchase|last_7d} and {date}, so a campaign name can carry its own 7-day ROAS. A client who opens Ads Manager reads the same number the agency reports.

What AdOps does not solve for agencies

There is no cross-account rule: a rule belongs to one ad account, and running the same policy across a book means creating it per account. Actions are campaign-shaped, so plan around campaign budgets. AdOps connects to Meta Ads only today, with TikTok Ads, Google Ads and Snapchat Ads listed as coming soon, and billing runs in Rupiah through Duitku.

Questions

Common questions about this use case

Can one AdOps rule use a different threshold per client?

Yes, through a custom metric. A custom metric names a Google Sheets spreadsheet id, a sheet name, a lookup column and a value column, and a condition can compare a live Meta metric against the resolved value instead of a fixed number.

How does AdOps keep one client's campaigns out of another client's rule?

Every rule belongs to an ad account and can be narrowed further by three pre-filters: Campaign ID, Campaign Name and Campaign Status. A Campaign Name contains filter turns an existing naming convention into the client boundary.

Does adding more ad accounts slow the automation down?

AdOps fans out one queued job per ad account, so accounts are processed as separate jobs rather than one long pass. The dispatcher itself only ever reads from the Meta Graph API; every write happens in the downstream worker.

How current is a number read from a Google Sheet?

Sheet rows are cached in memory on a 2-minute last-access TTL, with the last stored rows in MongoDB used as a fallback when the live fetch fails. A row that is read continuously can therefore serve a cached snapshot, so treat the sheet as a target list rather than a live feed.

Can an agency compare one metric against another instead of a fixed number?

Yes. A condition can be set to compare against a metric rather than a value, and the compared metric carries its own reporting period, so a rule can test today's Purchase ROAS against the last 7 days of Purchase ROAS on the same campaign.

Which parts of an AdOps account are locked if an invoice goes unpaid?

A suspended account keeps its data but shows a Feature Lock overlay across Automation Rules, Ad Accounts, Activity Logs and Custom Metrics until the invoice is paid. Account statuses are TRIAL, ACTIVE and SUSPEND.

Rebuild this playbook in your own account.

Every rule on this page is built from conditions and actions that ship in AdOps. Point them at one ad account and watch the log before you widen the scope.

Contact us See pricing

14 days free · no card required · billed in Rupiah

Inside the product

What the screens actually look like

Nine screens from the working dashboard — the rule builder, the metric picker, the dayparting grid and the log that records what happened. Scroll the strip.

  • The AdOps Performance Dashboard showing a Purchase ROAS card at 2.380x and an Aggregated ROAS card at 2.088x, both badged Profitable, a Monthly Budget card at Rp 155jt, and a Spend Breakdown ranking the top five ad accounts against a budget utilisation bar at 45.5 per cent.
    Performance Dashboard. Two ROAS figures to three decimals, a budget meter, and spend ranked by ad account — over Today, Last 7 days, Last 30 days or This month.
  • The AdOps rule list showing twelve automation rules, each with an on/off toggle, the ad accounts it manages, and when it last triggered — some minutes ago, others on a dated timestamp.
    Rule List. Every rule, what it manages and when it last fired. The toggle is the only thing between draft and live.
  • The AdOps condition builder showing a task with time-of-day conditions across seven day tags, a nested AND group holding a lifetime spend condition under 400,000, and a second task with four stacked metric conditions.
    Conditions. Metric, period, operator, value — joined with AND or OR, and nestable, so a rule can say something a dropdown cannot.
  • The AdOps metric picker open over a condition row: a panel with Meta Ads and Custom metrics tabs, a search box, and a scrolling list of metrics with Spend selected.
    Metric picker. Forty-five Meta metrics and your own sheet columns in the same list, each carrying a reporting period and any of six operators.
  • The AdOps dayparting timetable: a grid of hours against the seven days of the week, with the daytime hours filled navy for every day and the night hours and weekend evenings left empty.
    Dayparting grid. Or draw the hours instead. Anything outside the shape you fill in simply does not run.
  • An AdOps execution log detail: one campaign, two action panels badged Not Executed, each listing the action parameters and every condition evaluated with its actual value, its expected value and a Pass or Fail badge.
    Rule Log detail. Why a rule did nothing is recorded as carefully as why it did something — actual against expected, condition by condition.
  • The AdOps activity log listing budget increases and campaign renames, each row naming the affected campaign by id, the rule that caused it and how long ago it happened.
    Activity Log. One row per change AdOps made in your account, naming the campaign and the rule responsible.
  • The AdOps ad account list: eighteen Meta ad accounts with on/off toggles, account ids, Active or Inactive badges and this month’s spend in rupiah.
    Ad Accounts. Accounts discovered from Meta arrive switched off. Nothing is read, and nothing is changed, until you turn one on.
  • The AdOps custom metric editor mapping a Google Spreadsheet: a spreadsheet id, a sheet name, a column to match campaigns on and a column holding the values.
    Custom metric. Point AdOps at a sheet, name the matching column and the value column, and your own number joins the metric list.

Every figure is rebuilt from the product’s own interface and filled with invented data — no customer name, ad account or spend figure appears anywhere on this site. See how a run works