Architecture

How AdOps runs a rule

AdOps runs your Meta Ads rules as a two-part engine: a dispatcher wakes every 10 seconds and selects the rules that are due, then a worker reads the metrics each condition asks for, folds them with your AND or OR logic, applies the actions you chose, and writes one record per campaign for every run.

Nine stages sit between the clock tick that makes a rule due and the record that says what happened to each campaign. This page walks all nine, with the numbers that make each one concrete.

A 60-second film: one rule written, run by the AdOps engine, and recorded per campaign afterwards. Silent by design. 1 min No sound

The pipeline

From a clock tick to a written record.

Rule evaluation is split across two services connected by a Redis-backed queue. The dispatcher decides what is due and fetches the campaigns; the worker reads the metrics and executes the actions. Splitting them is why one slow ad account does not delay the rest of your rules.

  1. A sweep picks up the rules that are due

    A cron job wakes every 10 seconds and queries for rules whose status is ACTIVE, whose next scheduled execution time has passed, and whose start and end date window is open. The sweep is registered so a tick cannot start while the previous one is still running.

    Every 10s dispatch sweep

  2. Meta returns only the campaigns the rule scopes to

    The rule’s pre-filters — campaign id, a substring of the campaign name, or the campaign’s effective status — are serialised into the Graph API filtering parameter, so Meta narrows the list before anything crosses the wire. Status filters are translated into the set operators the Graph API expects.

    3 pre-filter fields

  3. Every page is followed, and a failed fetch is retried

    Entities are requested 1,000 to a page and each response’s paging cursor is followed until Meta returns no further page. A failing fetch is retried to a total of 3 attempts, waiting 1 second and then 2 seconds. If all three fail, that ad account is skipped and the rest of the rule continues.

    3 attempts, 1s then 2s apart

  4. Work is fanned out, one job per ad account

    Each ad account in the rule becomes its own job on a Redis-backed queue, carrying everything the worker needs: the access token, the rule document, the fetched entity list, the ad account id, and a batch id generated for that dispatch.

    1 job per ad account

  5. One batched Insights read, for exactly the metrics asked for

    The worker groups the task’s conditions by reporting period, de-duplicates the Insights fields each period needs, and issues them as a single Graph batch request per campaign. Metrics that are not Insights fields — time of day, custom metrics, daily budget, campaign age — are left out of that fetch and resolved separately. The batched read is sent with the unified attribution setting.

    1 batch request per campaign

  6. Custom metrics are resolved from your spreadsheet

    A condition set to a custom metric resolves against a Google Sheet: the spreadsheet id and sheet name you configured, a lookup column matched against the campaign id, and a value column read back as the number. Rows are held in memory for 2 minutes from last access, with a copy in MongoDB used when the live fetch fails.

    2 minutes in-memory sheet cache

  7. Conditions are folded with AND or OR

    Each condition resolves to a number and is compared with one of six operators against a fixed value, or against another metric that may use its own reporting period. The task’s conditions are then folded together with the group operator you chose, AND or OR, into a single pass or fail.

    6 operators, across 45 metrics

  8. Actions run against the Graph API

    Before acting, the worker checks the task’s cooldown: if the last executed result’s next-execution time is still in the future, it writes a skipped record and stops. Budget and name actions re-read the live daily budget or name from Meta before writing, and a budget change is clamped to the maximum or minimum you set rather than overshooting it.

    11 actions a task can take

  9. The run is written down

    Each campaign and task produces one execution record: the action, whether it executed, the parameters it ran with, the value before and after where something changed, every condition with the number it saw and whether it passed, the elapsed seconds of each internal step, and the next permitted execution time. Each dispatch also writes one batch record carrying the rule, the ad account, the item counts and the run status.

    1 record per campaign, per task, per run

In practice

What each stage means for you.

The same nine stages read from the outside — one line each, no architecture.

Stage What this means for you
A sweep picks up the rules that are due A rule set to a tight interval is picked up within seconds of becoming due, and a slow pass never fires the same rule twice.
Meta returns only the campaigns the rule scopes to On an account with thousands of campaigns, a rule scoped to one naming convention only ever looks at those campaigns.
Every page is followed, and a failed fetch is retried A momentary Graph API error costs a retry rather than a missed run, and one broken account does not stop the others.
Work is fanned out, one job per ad account One rule can cover every client account you manage, and each account is processed on its own without waiting for the others.
One batched Insights read, for exactly the metrics asked for A rule comparing today against the last 7 days costs one request rather than two, and the ROAS it reads is the one your ad set’s attribution window reports.
Custom metrics are resolved from your spreadsheet Targets kept in your own back-office sheet — margin, stock, a per-product ROAS floor — can drive a rule without being retyped into it.
Conditions are folded with AND or OR You can write “purchase ROAS over the last 7 days below 1.2 AND spend today above Rp 500.000” and read it back a month later without decoding it.
Actions run against the Graph API A rule that runs every 15 minutes can still raise a budget only once a day, and it cannot scale a campaign past the ceiling you gave it.
The run is written down When a campaign is paused you can see which condition crossed which threshold, what the number was, and what the budget or name looked like before and after.

Scheduling

How does AdOps decide when a rule is due?

Every rule stores its own next execution time. The dispatcher does not re-derive a schedule on the fly; it reads that timestamp and acts on it.

A check interval, in minutes
The next run is the current time plus the rule’s interval. If no interval is set, the rule falls back to 60 minutes.
A weekly timetable
A seven-day grid of times. The next slot later today is taken if one remains; otherwise the scan moves forward day by day, sorts that day’s slots and takes the earliest.
A start and end date
Either mode can carry a date window. The rule runs only while today falls inside it, so a promo rule expires without anyone remembering to switch it off.
Clock time as a condition
Separately from the schedule, a condition can gate on the hour and on a set of weekdays — the mechanism behind the shipped Night Budget Reset template.
Dispatch sweep
Every 10 seconds ticks never overlap
Default interval
60 minutes when a rule sets none
Reporting periods
11 chosen per condition
Action cooldowns
11 15 minutes to once in a lifetime
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. Sample data
Or draw the hours instead. Anything outside the shape you fill in simply does not run. Read the details

Boundaries

What the engine will not do on its own.

Automation that spends money needs limits that hold even when a rule is wrong. These are structural rather than settings you have to remember.

The dispatcher cannot change an ad
The service that decides what is due only ever issues read requests to the Graph API. Every budget change, pause and rename happens in the separate worker, so a bad deploy of the dispatcher cannot alter a campaign.
Nothing runs on an ad account you have not activated
Ad accounts discovered from Meta are stored inactive. Until you switch one on, no rule targets it and no spend is read from it.
A cooldown is checked before every action
Each task carries its own action frequency, from 15 minutes to once in a lifetime, independent of how often the rule itself is evaluated.
Budget writes are clamped
An increase stops at the maximum you set and a decrease stops at the minimum, by writing the boundary value instead of the projected one.
Live values are re-read before a write
Budget and name actions fetch the current daily budget or name from Meta immediately before changing it, rather than trusting the value carried in the job.
A skip is recorded, not swallowed
When a cooldown blocks an action, the engine writes an explicit skipped result with the reason and the time the action will next be allowed.
One failure does not end the run
A campaign that errors is logged and the batch continues. An ad account whose fetch fails is skipped and the rest of the rule proceeds.
There is a platform-wide stop
An environment flag checked at the top of every sweep freezes all automated ad actions, for use during a Meta incident or a maintenance window.

Evidence

What can you check after a run?

One record per campaign, per task, per run. The rule log detail screen renders it field by field.

Field What it tells you
Executed, Not executed, Skipped A badge on each task, carrying the skip reason where one applies.
Action parameters The exact values the action ran with — type, amount, cap, separator, name template.
Before and after The old and the new value for actions that changed a budget or a name.
Condition results Every condition with the number the engine saw and whether it passed.
Execution time Total elapsed time is stored with every record; the per-step timeline is rendered for ADMIN accounts.
Next execution When the task will next be allowed to run for that campaign.
Batch id The correlation id that ties every record from one dispatch together.
One run, three tasks, one budget change from 150,000 to 250,000. The log records all three, not only the one that fired. No sound. 8 sec No sound

See it run against one ad account.

Start from a template, point it at a single account, and leave the rule in draft until the numbers look right. Nothing touches your campaigns until you set it live.

Read the security page See pricing

14 days free · no card required

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