Sift Sift
Breathe first. Scroll later.

Dashboard

What happened each day — and whether it is trending up or down.

→
Totals are the account, UTC calendar days, for the window picked above. The delta pill compares against the immediately preceding equal window — no pill means the prior window has no data to compare against. For Cancellations and Expirations a rise is bad and is coloured so. Spend and installs are Apple; trials, conversions, cancellations and expirations are RevenueCat (production only — sandbox is never counted).

Daily Briefing

What moved, what to do, what to leave alone.

AI Briefing

🌿

Since yesterday

    Needs attention

      Running & next experiments

      Experiments

      Your ASA lab notebook — every theory, the change that tests it, and the verdict.

      This is the layer Apple's console and a plain dashboard don't have: a hypothesis ledger that survives the ~24h reporting lag and a 7-day runway, and turns each finding into a reusable rule.

      🌱 Learnings & rules adopted

      The durable knowledge experiments leave behind — these constrain every future recommendation.

        Campaigns

        One row per campaign. Click a header to sort.

        →
        TOTALS is the account total, not the sum of the rows above — Apple masks low-volume terms, so per-campaign rows can under-count. — means we hold no data for that cell; 0 means we hold data and it is zero. Columns up to Spend are Apple; Tracked onward are RevenueCat, and Tracked is never the same number as Inst. Serving is Apple's answer to “is this actually running?”, not the status you set — the two disagree routinely, and a badge with no reason chips means Apple gave us no reason.

        Purchases

        Every RevenueCat purchase, newest first, with the campaign each is attributed to.

        →
        unattributed — resolving is the honest label while AdServices has not answered yet — it self-fills once the install resolves (or stays, for an organic payer). A sandbox row is a test purchase: visible here so testing is verifiable end-to-end, but never counted as revenue. Dates are inclusive UTC days.

        Keywords

        Last 7 days · band by TTR · click a header to sort.

        →
        🟢 ≥4%   🟡 2–4%   🔴 <2% TTR — banded on the unrounded rate, so a value shown as 4.00% can sit in the amber band. A keyword with no impressions has no rate to band, so it carries no dot; the ○ No impressions chip filters to those rows. Rows under ~100 impressions are directional only. CPA shown where installs > 0.

        Search Terms

        What people actually typed — the source of negative-keyword discovery.

        Reports

        Pick a dimension, a window and a timezone; filter it; sort any column. Save it and it comes back.

        →
        TOTALS is the account total, not the sum of the rows above — Apple masks low-volume terms, so per-entity rows can under-count. — means we hold no data for that cell; 0 means we hold data and it is zero. TTR, CR, CPA and CPT are re-derived from the summed counts, never averaged from the daily rows.

        All reports

        A saved report stores the query, never the numbers — opening one re-runs it against today's data.

        Timeline

        Every account change on one axis — so you can read results against the change that caused them.

        Blended TTR, with change marker

        Change log

          AI Usage

          Every call we made to a model, what it cost, and why.

          Cost is computed at the moment of the call and stored — never re-derived from today's price list, so a price change cannot rewrite what you already paid. Shown to six decimals because a single call routinely costs less than a cent.

          Data & Sync

          Where this account's data came from, and when.

          Sync history

          Every run this account has made — scheduled and manual alike.

          One tick of the schedule writes four rows — sync, eval, experiment eval, alert — so “did we pull fresh data?” and “did we form suggestions?” stay separately answerable. Rows is what that job wrote: metrics for a sync, suggestions for an eval, alerts for an alert.

          The pipeline

          ① Apple Ads API v5
          Read-only pull: campaigns, ad groups, keywords, search-term reports.
          →
          ② MySQL
          Per-day metrics, reconciled against Apple's own grand totals before they land.
          →
          ③ Evaluate
          Deterministic rules form suggestions and raise alerts. No model is called in this phase.
          →
          ④ This view
          Briefing, experiment ledger, scorecard.

          Webhook deliveries

          Every inbound RevenueCat delivery, newest first — and what Sift did with it.

          Admin-only. A rejected (401) delivery is logged with no body — so a mis-set REVENUECAT_WEBHOOK_SECRET shows up here, not only in the server logs. The Payload of an accepted delivery is the body as received, secret-scrubbed. Rows past the retention window are swept nightly.

          Remote Config

          What the shipped app is told at launch — changeable without a release.

          Live payload

          Exactly what GET /api/v1/app-config returns for this account right now, from the same code path — so it cannot disagree with what the app receives. A disabled key is absent, never null: absence means the binary uses its compiled default, and {} is a complete, correct answer for an account with nothing turned on.

          Loading…

          Keys

          Every key on the account, enabled or not — a staged-off key is the thing you came here to turn on, so hiding it would hide the workflow. Enabled is the staging mechanism: there is no draft/publish step, so turning a key on is what puts it in the payload above on every client's next launch.

          Deleting a key does not restore a previous value — it reverts every client to its compiled default on the next launch, the same blast radius as a value change and easier to do by accident. Only the audit trail keeps the last value. Reordering rules is a config change too: first match wins, so moving one above another can invert the answer for a whole storefront.

          Deliveries

          Every launch that asked this account for its config, newest first. A 304 is a successful delivery — a warm cache revalidating — and it means that device is actively running that version. Click a version to see the exact document it carried.

          Every delivery is recorded — no sampling, and nothing is ever deleted. So an empty result means no such request was served, provided the delivery log has been on and healthy for the window you are looking at. A version with no payload to expand was still served: the account's snapshot budget was exhausted, or that write failed.

          Add a rule

          A default plus an ordered list of overrides — first match wins. New rules are appended last and staged off. (The AND-only rule and the eight-condition cap are stated under the conditions themselves, so the editor on an existing rule says the same thing in the same words.)
          ⚠ Do not write the negative. country ≠ CA → false is not a rule, it is the key's value. Encoding both directions is how a rule set acquires two rules that disagree and a serving order nobody can reconstruct.

          Add a key

          The key and its type are immutable after creation — a key that changes type mid-flight is a decoding crash in a binary already on people's phones that cannot be patched. To change a type, add a new key and retire the old one. Type the value the way JSON carries it: a string as-is with no quotes, an int or float as a number, a json value as JSON.

          Alerts

          What the pipeline noticed — and what has already been acknowledged.

          Acknowledging is one-way. There is no way to un-acknowledge an alert and no manual resolve. Only the rule-driven types — CPA spike, budget capped, keyword drag, TTR drop, spend anomaly — are ever auto-resolved, and only while they are still open, so sync failure, sync stale and experiment due stay acknowledged for good: the Acknowledged list only grows. That is the design, not a sweep that has stopped running.
          Opening this screen is what raises — and what clears — the pipeline heartbeat. sync stale is the one alert no background job can raise, because the failure it reports is the job not running, so it is evaluated when this screen reads the inbox. The check runs both ways: while the data is stale a row is raised, and the moment the pipeline is healthy again every still-open sync stale row is moved to resolved — by the read itself, on the spot. So a sync-stale row that appears the moment you look, or that reads resolved with a timestamp from a second ago, is the check working rather than a coincidence. It is the one status change here that nobody clicked, and All statuses is the view it is visible in. Acknowledging needs the operator role or higher; a viewer sees the inbox without the button.

          Suggestions

          What the engine would change, why, and what it expects to happen.

          Read-only, on purpose. There is no approve or reject: the statuses exist in the model and nothing in Sift moves a row into them — that write is Phase 2, the money-moving surface. A control that can only fail is worse than no control, so this screen has none. The one transition Phase 1 performs on its own is expiry.
          A suggestion is a proposal, not a change: nothing here has touched a bid, and nothing here will. The queue exists so the decision is made by a person looking at the evidence.

          Settings

          Your account and password, the provider keys Sift holds, and the API reference for Claude Code.

          Credentials

          Provider API keys for the vault. The secret travels inbound only — it is verified against the provider before it is stored, and what comes back is a masked fingerprint, never the key itself. Submitting for a provider that already has a key rotates it.

          Deleting destroys Sift's copy of the key; the row is kept for audit and can be replaced by adding the provider again. It does not revoke the key at the provider — do that in their console too.

          Change password

          Changing your password signs out every other device immediately. You will stay signed in here.

          At least 12 characters, and not one that appears in a breach list.

          API reference for Claude Code

          A single Markdown file describing every Sift endpoint and the conventions a client needs — auth, money units, the report filter grammar, rate limits, and what it can and cannot do. Hand it to the Claude Code CLI so it can drive Sift over HTTP instead of guessing.

          New experiment

          A theory is only real once it's written down with the change that tests it.

          Change a value

          Type the value the way JSON carries it: a string as-is with no quotes, an int or float as a number, a json value as JSON. The line above quotes a string value so you can tell "5" from 5 — the field never does.