CORS policy
The Future Demand API permits cross-origin requests only on the unauthenticatedGET /status/ endpoint. Every authenticated endpoint
rejects browser CORS.
This is deliberate: credentials must not leak to a browser. Authenticated
calls must go through your own backend (or use short-lived access tokens
served from your origin).
The proxy pattern
- Holds the long-lived refresh token (or service-account credentials).
- Manages session refresh.
- Strips browser cookies/headers and forwards only what’s needed.
- Adds
Authorization: Bearer <access_token>andX-Preferred-Partner-Id: <partner_id>per the calling user’s selected partner.
Two-host architecture
The reference webapp talks to two Future Demand hosts:| Host | Base URL | Purpose | Used by |
|---|---|---|---|
| Main API | https://client-api.prd.future-demand.com/api/v3 | The bulk of endpoints — events, recommendations, campaigns, packages, … | Almost every guide. |
| CR API (Customer Representation) | ask your account contact — separate host, distinct from client-api.* | Event ingest, simulation, bulk upload, the second step of status-update. | Event Editor, Bulk Upload, Simulation, the cross-host status update on Event Detail. |
Which calls go to which host
Calls that go to the CR API (from the reference webapp):GET /events/{eid}— only when loading raw event payload for the Event Editor (includeslineups_raw,description).PUT /events/{eid}?event_type=...— Event Editor save.POST /events/?event_type=...— Simulation create.POST events/bulk_upload?event_type=...— Bulk event upload (note: no leading slash on this path in the reference webapp).PUT /events/last_updated_on_fe/{eid}— second half of everysetFrontendStatus(flag / archive).
POST /media — the other path quirk
POST /media (creative upload) goes to the main API host but without
the /v3 prefix. The reference webapp uses a third bare axios instance
for this single endpoint. Replicate or you get a 404.
Token-bound sessions for embedded widgets
The reference webapp also supports two link-based session modes (used for embedded widgets, not the main webapp flow):X-Partner-Token: <token>— read fromsessionStorage["partner-session"]. Used by partner-link flows.X-Auth-Code: <token>— read fromsessionStorage["anonymous-session"]. Used by anonymous embed flows.
Authorization: Bearer <token>.
Per-user vs server-to-server proxying
| Mode | Refresh token | Per-user audit |
|---|---|---|
| Server-to-server | Stored in your backend secret store. One service account acts for all users. | No per-user audit on Future Demand’s side. |
| Per-user | One refresh token per user, server-managed via HttpOnly cookie. | Yes. |
Common mistakes
Calling Future Demand directly from the browser
Calling Future Demand directly from the browser
Will fail at CORS for every authenticated endpoint. Proxy through your
backend.
Mixing main and CR API base URLs
Mixing main and CR API base URLs
Easy to do if you have one global axios instance. Use two distinct
instances and make the second one obvious in your code.
Forgetting `/v3` on main API or adding it to CR API
Forgetting `/v3` on main API or adding it to CR API
Main needs
/v3, CR doesn’t. The POST /media exception adds further
confusion — keep the special case isolated.Leaking refresh tokens to the browser
Leaking refresh tokens to the browser
Use HttpOnly cookies or keep refresh entirely server-side. The browser
should only see the short-lived access token (or nothing, if you proxy
fully).