Security

How AdOps handles your data

AdOps holds your Meta access token on its own servers and uses it to call the Graph API on your behalf. Every request to the AdOps API needs a bearer token, rule definitions and execution records live in MongoDB, and disconnecting Facebook clears the stored token in one action.

This page describes the mechanisms that exist today. It does not make promises about them, and it does not claim a certification, an independent audit, or an uptime figure.

Access

Who can reach your account?

One authentication scheme, two roles, and a default that closes a new endpoint rather than opening it.

Bearer tokens on every request
Signing in issues a JSON Web Token carrying the user id, username, roles and account status, with a 24-hour expiry. A guard registered globally verifies that token on every request and loads the user onto it.
Public routes are explicit
Only routes decorated as public bypass the guard: sign-in, signup, email confirmation, the payment callback and the health check. Everything else requires a token by default.
Two roles: USER and ADMIN
Accounts carry a roles array. The list endpoints for rules, custom metrics and invoices force the query to the caller’s own account id unless the caller is an ADMIN, and the dashboard reads the same token to gate admin-only controls.
Password rules at signup
A password is 8 to 128 characters and must contain an uppercase letter, a lowercase letter, a digit and a symbol, with a matching confirmation field. The confirmation email carries a 64-character token and a 60-second resend cooldown.
Account status gates the product
An account is TRIAL, ACTIVE or SUSPEND. A suspended account keeps its data but sees a lock over rules, custom metrics, ad accounts, activity and integrations until the invoice is paid.

The Meta token

Where your Meta access token lives.

Connecting Facebook stores an access token on your user record on the AdOps server. Every Graph API call AdOps makes for you — reading campaigns, reading Insights, changing a budget, pausing a campaign — is sent with that token in an Authorization header. There is no shared or pooled credential: your rules run under your own Facebook authorisation.

Connecting verifies the token first. AdOps calls Meta’s /me endpoint to confirm the token works and to capture the profile name, email and picture shown on the Integrations screen, so a bad or expired token fails at connect time rather than silently days later.

The profile and connection-status endpoints return a connected flag plus that non-sensitive metadata — id, name, email, picture, connected date, last verified date — rather than the token itself.

Disconnecting, from the Integrations screen and behind a confirmation, deletes every ad-account record belonging to your account and sets the stored token, the connected flag and the Facebook profile metadata to null in the same call.

Ad accounts discovered from Meta are stored inactive. A rule cannot target an ad account, and no spend is read from it, until you activate that account explicitly.

Stored
Server-side on your user record
Sent to
Meta Graph API as a bearer header, on your behalf
AdOps session token
24 hours JWT expiry
Disconnect
One action removes ad accounts, clears the token

Storage

What AdOps stores in MongoDB.

The management API and the two execution services share one MongoDB database. This is what a working account puts in it.

Record What it holds
Rule definitions Name, target ad accounts, pre-filters, the tasks with their conditions and actions, the schedule, and the next and last execution times.
Execution results One document per campaign, per task, per run: the action, whether it executed, the action parameters, the value before and after, every condition with its resolved number, per-step timings, and the next permitted execution time.
Batch records One per dispatch: the rule, the ad account, start and end times, how many items were applied and affected, and the run status.
Ad-account metadata Account id, account name, an active flag, and a spend snapshot with its currency and date range.
Custom metric definitions The spreadsheet id, sheet name, lookup column and value column a metric reads.
Cached spreadsheet rows The rows last read from each Google Sheet, keyed by file id and sheet name, used when a live fetch fails.
Account records Full name, username, email, confirmation state, roles, status, trial dates, company name, industry and monthly ad-budget band — and the Meta access token.
Error records Function name, a normalised error message, the stack, the request parameters and the API response, an occurrence count, and first and last seen times.
AI generation logs For each AI rule generation: the request, the response, the response time, the confidence score, and the rule it belongs to.
Email, payment and invoice records Sent emails with their template and status; payment-gateway calls with their request and response bodies; invoices with amount, due date and status.

The only campaign data AdOps reads from Meta is entity names and creation times, plus the aggregate Insights figures your conditions ask for. It does not read or store advertising creative, audience lists, or customer records.

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. Sample data
Why a rule did nothing is recorded as carefully as why it did something — actual against expected, condition by condition. Read the details

Failures

How AdOps records its own failures.

Errors are written to the shared database rather than only to a console. Before storage the message is normalised: timestamps, UUIDs, IP addresses, ad-account ids and any run of ten or more digits are replaced with placeholders, so the same fault reads the same way every time.

Errors are then grouped by function name plus that normalised message, with an MD5 fingerprint stored alongside. A repeat increments an occurrence count and updates the last-seen time instead of adding a row, and each group keeps the last 10 request-parameter samples and the last 10 API-response samples. An error marked resolved that recurs is reopened automatically.

Every write AdOps makes to the Graph API goes through one wrapper, so a failed budget change, pause or rename is captured with the request body and Meta’s own error response attached. If the deduplication path itself fails, a plain record is written instead — a logging problem does not stop the worker.

Operations

How AdOps runs.

Deployment facts, chosen for what they say about how much one mistake can do.

Containerised services
The execution services ship as multi-stage images built on node:22-alpine, installing production dependencies only in the final stage and running as a non-root user, with tini as the init process and PM2 managing the processes.
Reads and writes are separate services
The dispatcher that decides which rules are due only issues read requests to the Graph API. Every write — budget, status, name, duplication — happens in the worker, which is a different process.
A platform-wide stop
An environment flag is checked at the top of every dispatch sweep. Set to anything other than true, the sweep returns immediately and no automated ad action is taken anywhere.
Queue and connection retries
Jobs retry with exponential backoff from 1 second, MongoDB reconnects on 3 attempts a second apart, and Redis reconnects on a backoff capped at 2 seconds.
Outbound calls are bounded
The worker’s HTTP client uses a 30-second timeout, and so does the Google Sheets fetch. A hanging dependency ends as a recorded error rather than a stuck run.
The AdOps activity log with its filter panel open, showing six filters — event name, ad account, rule name, entity id, entity name and action — with two of them filled in. Sample data
Six ways to narrow a hundred thousand events down to the one change you are trying to account for.

Limits

What this page does not claim.

AdOps does not hold a SOC 2, ISO 27001 or equivalent certification, and this page does not claim one. No third-party penetration test has been published. No uptime figure is published, and none is implied.

Nothing on this page is a warranty. What AdOps is contractually obliged to do is set out in the Terms of Service and the Data Processing Agreement; where those documents and this page differ, those documents are the ones that count.

If a statement here matters to your decision, ask us to point at the mechanism behind it before you rely on it.

Disclosure

Reporting a vulnerability.

If you believe you have found a security problem in AdOps, email support@adops.id with the details.

What to include

  • What you found, and where.
  • The steps to reproduce it.
  • The impact you believe it has.
  • Any account, rule or request ids involved.

Please test only against your own account. Do not run denial-of-service tests, do not send automated scanning traffic, and do not access data belonging to anyone else.

AdOps does not run a bug-bounty programme and does not pay for reports.

Connect one ad account and watch what happens.

Ad accounts arrive inactive, rules start as drafts, and every run writes a record. You can see the whole path with one account switched on and one rule live.

Contact us Data processing agreement

14 days free · disconnect at any time

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