Choose Platform

Reference harness · staging external_ref: —
STEP 1

Choose a platform

Pick a platform + set your external_ref (your namespace for addressing end-users — isolates your test data). Then bind.

Not a secret (IN-6 — your own addressing namespace). Auto-generated per browser session so concurrent testers don't bind/overwrite each other's accounts; edit it to reuse a specific one. Kept across the OAuth redirect round-trip.

STEP 2

Request sent to binding

Exactly what this app's backend sent to /api/v3/oauth/start (key held server-side, never sent to this page) — and what it returned.

OAuth start

GET /api/v3/oauth/start
Loading example…

Click Bind above to see a live run here.

STEP 3

Auth result

The token is exchanged + stored server-side by BDDAI — never returned here. The redirect back only ever carries ?status=.

Loading example…

Complete a Bind above to see a live result here.

STEP 4

Bound accounts

GET {service_base}/api/v3/public-access/{platform}/ad-accounts?external_ref= — scoped to this end-user.

Loading example…

Complete a Bind above to see a live fetch here.

STEP 5

Fetch data

Insights family only (meta/instagram/tiktok/google/google_analytics — shopee/shopify are orders-family, not wired here) — GET /{platform}/data for one account, no date range given (service defaults to the last 30 days).

SV-C6 landed — period (day/week/month/year) and breakdown/filter are now server-side (breakdown_interval/group_by/ filter_by+filter), not client-side aggregation like before. Period comparison (current vs previous, delta) is still fetch-two-ranges + compute client-side — no built-in compare param.

ℹ️ filter matches on campaign name (regex substring, case-insensitive) — not a numeric campaign ID. Single value: verified correct (narrows the summary exactly as expected, and filter_exclude gives the exact complement). Known issue (found live, not a demo bug): multiple values are currently ANDed together server-side, not OR'd — filtering by two distinct campaign names returns empty instead of their combined total. Use one value at a time for now.

Loading example…

Complete step 4 above to pick an account here.

REFERENCE

Metric catalog

Own platform picker below — independent from Step 1's Choose Platform, no bind needed. Trace any platform's metric list/config directly. Owner decision: metric access is not gated per-metric — once a platform is granted, its whole catalog is open (no per-metric allowlist below).

⚠️ Catalog = full intended metric list. Actual availability on /{platform}/data fully aligns once service's MV-1 lands (TikTok is currently a subset) — source moves to MV-2 (data-driven) later.

Loading…
REFERENCE

API Contract & Schema

Direct endpoints your backend calls. Base = per-environment host below + path. Every call is server-side with your key.

Base URLs (per environment)

PlaceholderServiceStagingProduction
{binding_base}Binding · OAuth broker (ingest)https://binding.stg.bdd.aihttps://binding.bdd.ai
{service_base}Service · Public Access (consume)https://service.stg.bdd.aihttps://service.bdd.ai

Legacy *.up.railway.app / *.devbdd.com = deprecated. Reference harness (this demo): staging demo.broker.stg.bdd.ai · prod demo.broker.bdd.ai. Your app integrates directly with {binding_base}/{service_base} — this demo is a reference harness only, not an integration target.

Authentication

Authorization: Bearer pk_live_…    # purpose='ingest' · server-side only

Guard order: missing/invalid scheme → 401 · unknown/inactive → 401 · expired → 401 · IP not allowed → 403 · platform ∉ allowed_platforms403 · source suspended → 403.

Endpoints

6 direct
#Method · PathKey paramsResponseErrors
APOST {service_base}/api/v3/public-access/usersexternal_ref200 {user_id}401·403·422
BGET {binding_base}/api/v3/oauth/startplatform·source_app·return_url·external_ref302 → consent400·401·403·404·503
C1GET {service_base}/…/{platform}/ad-accountsexternal_ref200 {data:[…]}401·403·404·422·429
C2GET {service_base}/…/{platform}/dataaccount_id·external_ref·date_start·date_end·breakdown_interval·group_by·filter_by·filter200 {data,pagination}401·403·404·422·429
C3GET {service_base}/…/{platform}/metrics(platform-gated, no ref)200 {data:[…]}401·403
C4GET {service_base}/…/{platform}/orders/{overview|periodic|distribution}account_id·external_ref·breakdown_interval200 (gated 503)401·403·404·422

Response schema — /data (insights)

{ "data": { "spend": 48200000, "impressions": 3400000, "clicks": 92100 }, "pagination": null }
# no dimension flags -> 1 aggregate dict. KPI cards.
{ "data": [ { "date": "2026-07-01", "spend": 1600000 }, … ], "pagination": { "page":1, "page_size":100, "total":30, "total_pages":1 } }
# show_date=true OR breakdown_interval=day|week|month|year -> row per-period.
{ "data": [ { "campaign": "Sneakers", "spend": 12000000 }, … ],
  "pagination": { "page":1, "page_size":50, "total":7, "total_pages":1 } }
# group_by/campaign/objective -> row per-dimension (paginated). Ad-level device/placement = facebook-only.

Required: account_id+external_ref. Date defaults to the last 30 days; max span 92 days; page_size ≤ 1000. Data is warehoused, not a live-API passthrough.

REFERENCE

Errors & Platform Vocabulary

HTTP status meanings + the bind↔consume platform-string mapping (the guard maps them automatically).

Error reference

StatusMeaning
400Raw user_id on external path, or unknown platform value.
401Missing / invalid / expired key (or JWT on internal path).
403purpose≠ingest · platform ∉ allowed_platforms · return_url not allowlisted · source suspended · cross-source ref · account not owned.
404source_app not found · external_ref unknown for this source · unknown platform/report.
422external_ref empty/too long · key not scoped · return_url not absolute · invalid date range / page_size.
429Rate-limited (respect Retry-After).
503Platform wired but gated (e.g. Shopee).

Platform vocabulary & availability

bind platformgrant allowed_platformsconsume pathStatus
googlegoogle_ads/gadwords/…LIVE
google_analyticsgoogle_analytics/ganalytics/…LIVE
metafacebook/facebook/…LIVE
instagraminstagram/instagram/…LIVE
tiktoktiktok/tiktok/…LIVE
shopeeshopee/shopee/ordersGATED 503
shopifyshopify/shopify/ordersDISABLED