Wrike Media Connect

An embeddable JavaScript widget that surfaces Wrike campaign data inside any web application — no framework, no build step required.

The widget is distributed as a single .js file served from a central HTTPS server. Host applications embed it with a standard <script> tag, configure it through data-* attributes, and react to campaign events through the browser's native Custom Events API.

display

Display Campaign

Fetch and render live campaign data from Wrike with configurable fields.

update

Update Campaign

Display current campaign data and let users push changes back to Wrike.

create

Create Campaign

Submit a pre-configured payload to create a new Wrike campaign.

ℹ️
Style isolation. The widget renders inside a Shadow DOM. Host-page stylesheets cannot bleed in, and widget styles cannot bleed out. Multiple independent instances can coexist on the same page.

Quick Start

Add the widget to any HTML page in two steps:

1. Add a container element

Place an empty <div> where you want the widget to appear. Give it a unique id.

<div id="wrike-campaign-1"></div>

2. Include the script tag

Add the <script> tag immediately after (or anywhere after) the container element. Point data-target at the container's id and supply the campaign ID.

<div id="wrike-campaign-1"></div>
<script
    src="https://your-server.example.com/wrike-media-connect.js"
    data-target="wrike-campaign-1"
    data-campaign-id="IEAC7PRTI5SVIV6V"
></script>

On load, the widget checks for a stored OAuth token. If none exists, it renders a "Connect to Wrike" button that opens the authentication popup when clicked. After a successful login the widget immediately fetches and displays campaign data.

Try it first. Open demo.html in a browser for a working example with a live code editor, test panel, and event log.

How It Works

Script loading

Each <script> tag runs an IIFE (Immediately Invoked Function Expression). The IIFE reads document.currentScript synchronously at the top — this reference becomes null inside async callbacks, so it is captured into a local variable immediately. This means you can include the same wrike-media-connect.js file multiple times with different data-* configurations; each invocation is fully independent.

Authentication

  • The widget reads wmc_oauth_token from localStorage.
  • If the token is absent, it shows a login prompt.
  • Clicking the button opens an OAuth popup to the Wrike XPI authorization endpoint.
  • After the user grants access, the popup exchanges the authorization code for a token via callback.html.
  • The token is written to localStorage and broadcast to all widget instances via BroadcastChannel so every widget on the page updates without a page reload.

Token sharing across widgets

All widgets on a page (and in other tabs of the same origin) share a single OAuth token stored under wmc_oauth_token in localStorage. A BroadcastChannel named wmc_auth_channel carries wmc_token_changed notifications so every widget re-renders immediately whenever the auth state changes — including across browser tabs.

Display Mode

The default mode. The widget fetches the specified campaign from the Wrike XPI API and renders the fields listed in data-display-attr. A Refresh (🔄) button lets users reload data on demand. If the campaign has a permalink, an Open in Wrike (↗) button appears and opens the campaign in an in-page modal iframe.

example — display.html
<div id="wrike-campaign-1"></div>
<script
    src="./wrike-media-connect.js"
    data-target="wrike-campaign-1"
    data-campaign-id="IEAC7PRTI5SVIV6V"
    data-display-attr='["campaignname","campaignstartdate","campaignenddate","status","client","debtor"]'
    data-callback-attr='["campaignname","client","debtor","campaignbudget"]'
    data-color-primary="#1E40AF"
    data-color-background="#FFFFFF"
    data-color-text="#111827"
    data-color-accent="#3B82F6"
></script>

Listening for data

Subscribe to wrike-media-connect:data on the container element to receive campaign field values whenever the widget fetches or refreshes:

const el = document.getElementById('wrike-campaign-1');

el.addEventListener('wrike-media-connect:data', (e) => {
    // e.detail contains only the keys listed in data-callback-attr
    console.log(e.detail);
    // { campaignname: "World Peace Day 2026", client: "DEMO-Bayer", ... }
});

Update Mode

In update mode the widget fetches and displays the current campaign data exactly as in display mode, and adds an "Update to Wrike" button. When clicked, the widget sends a PUT request to the Wrike XPI API with the fields specified in data-update-payload.

example — update.html
<div id="wrike-campaign-update"></div>
<script
    src="./wrike-media-connect.js"
    data-target="wrike-campaign-update"
    data-mode="update"
    data-campaign-id="MQAAAAEH0egx"
    data-update-payload='{"campaignbudget":"11","campaignname":"World Pinball Day 23","client":"Adidas Group"}'
    data-display-attr='["campaignname","campaignstartdate","campaignenddate","campaignbudget","client","debtor"]'
    data-callback-attr='["campaignname","campaignbudget"]'
></script>

Update flow

  1. Widget fetches and displays current campaign data.
  2. User reviews the data and clicks "Update to Wrike".
  3. Button is disabled and shows "Updating…" while the request is in flight.
  4. On success: a green "Campaign updated successfully" banner appears above the updated data. A wrike-media-connect:updated event fires on the target element.
  5. User clicks to dismiss the banner. The widget returns to a clean read-only display of the updated data.
  6. On failure: an inline error message appears and the "Update to Wrike" button is restored.

Supplying the update payload dynamically

The payload can also be set at runtime by the host application — useful when the fields to update depend on user input elsewhere on the page. Set data-wmc-update-payload on the container <div> before the button is clicked; the widget reads it at click time and it takes precedence over data-update-payload.

// Host app collects user input and sets the payload dynamically
const newBudget = document.getElementById('budget-input').value;

document.getElementById('wrike-campaign-update').dataset.wmcUpdatePayload =
    JSON.stringify({ campaignbudget: newBudget, campaignname: "Updated Name" });

// User then clicks the "Update to Wrike" button inside the widget normally.
ℹ️
Priority: data-wmc-update-payload (runtime) overrides data-update-payload (inline). If neither is set when the button is clicked, the widget shows an error and fires a wrike-media-connect:error event with code UPDATE_PAYLOAD_MISSING.

Create Mode

In create mode the widget renders a "Create Campaign" button. Clicking it sends a POST request to the Wrike XPI API with the complete campaign configuration supplied in data-creation-payload.

💡
Use the {{timestamp}} placeholder anywhere in the payload string. The widget replaces it with the current ISO 8601 timestamp at the moment of submission, which helps ensure unique campaign names.
example — create.html
<div id="wrike-campaign-create"></div>
<script
    src="./wrike-media-connect.js"
    data-target="wrike-campaign-create"
    data-mode="create"
    data-creation-payload='{
        "type": "Campaign Request",
        "space": "WPPM-MKT1-STAGING (DEU)",
        "entity": "Campaign",
        "variantId": 6,
        "fields": {
            "spacename": "WPP-DEMO-GMN",
            "requestormarket": "Other",
            "debtor": "DEMO-Bayer-Aspirina",
            "currency": "EUR",
            "client": "DEMO-Bayer",
            "campaignstartdate": "2026-08-29",
            "campaignobjective": "Upper Funnel / Awareness",
            "campaignname": "World Peace Day {{timestamp}}",
            "campaignenddate": "2026-10-29",
            "campaignbudget": "100",
            "selectedchannels": ["Paid Search", "Programmatic"]
        }
    }'
></script>

Create flow

  1. Widget renders a "Create Campaign" button.
  2. User clicks the button. The button shows "Creating…" and is disabled.
  3. On success: the widget shows the new campaign's ID and a link to open it in Wrike. A wrike-media-connect:created event fires.
  4. A "Create Another" button resets the widget to its ready state.
  5. On failure: an inline error is shown and the button is restored.
const el = document.getElementById('wrike-campaign-create');

el.addEventListener('wrike-media-connect:created', (e) => {
    console.log('New campaign ID:', e.detail.id);
    console.log('Permalink:', e.detail.permalink);
});

Attribute Reference

Common attributes (all modes)

Attribute Type Required Description
data-target String Required The id of the container <div> the widget will render into. Must match an element that exists in the DOM before the script executes.
data-mode "display" | "update" | "create" Optional Controls the widget's behavior. Defaults to "display" when omitted.
data-color-primary CSS Color Optional Header background color. Default: #1E40AF
data-color-background CSS Color Optional Widget body background color. Default: #FFFFFF
data-color-text CSS Color Optional Primary text color. Default: #111827
data-color-accent CSS Color Optional Button and link color. Default: #3B82F6

Display & Update mode attributes

Attribute Type Required Description
data-campaign-id String Required The Wrike campaign ID to fetch and display (e.g. IEAC7PRTI5SVIV6V).
data-display-attr JSON Array Optional JSON array of API field keys to render as rows in the widget body. Default: ["campaignname","campaignstartdate","campaignenddate","status","client","debtor"]
data-callback-attr JSON Array Optional JSON array of API field keys to include in the wrike-media-connect:data event payload. If omitted, the event detail is an empty object.

Update mode attributes

Attribute Type Required Description
data-update-payload JSON Object Optional* JSON object of field key/value pairs to send in the PUT request. Only the fields included here are updated; all other campaign fields are untouched.

*Required unless the host application sets data-wmc-update-payload on the target element at runtime before the button is clicked.

Create mode attributes

Attribute Type Required Description
data-creation-payload JSON Object Required The complete campaign creation payload sent as the POST request body. Supports the {{timestamp}} placeholder, which is replaced with the current ISO 8601 timestamp at submission time.

Custom Events

All events are dispatched on the target element (document.getElementById(data-target)) using the standard CustomEvent API. Subscribe with addEventListener on the container element.

wrike-media-connect:ready
all modes

Fired once when the widget has initialized and attached its Shadow DOM. No detail payload.

el.addEventListener('wrike-media-connect:ready', () => {
    console.log('Widget is ready');
});
wrike-media-connect:data
display update

Fired after each successful campaign fetch (initial load, manual refresh, or after an update). e.detail contains only the keys listed in data-callback-attr.

el.addEventListener('wrike-media-connect:data', (e) => {
    // e.detail only includes keys listed in data-callback-attr
    const { campaignname, campaignbudget } = e.detail;
    updateMyDashboard(campaignname, campaignbudget);
});
wrike-media-connect:updated
update

Fired after a successful PUT request. e.detail is the full campaign data object returned by the API.

el.addEventListener('wrike-media-connect:updated', (e) => {
    console.log('Campaign updated. New data:', e.detail);
    // e.detail: { campaignname, campaignbudget, campaignid, ... }
});
wrike-media-connect:created
create

Fired after a successful campaign creation. e.detail contains the new campaign's id and permalink.

el.addEventListener('wrike-media-connect:created', (e) => {
    const { id, permalink } = e.detail;
    console.log(`Created campaign ${id} — ${permalink}`);
});
wrike-media-connect:error
all modes

Fired when any API call fails. e.detail includes a code string and a human-readable message.

el.addEventListener('wrike-media-connect:error', (e) => {
    const { code, message } = e.detail;
    // Possible codes:
    //   CAMPAIGN_FETCH_FAILED      — GET request failed
    //   CAMPAIGN_UPDATE_FAILED     — PUT request failed
    //   CAMPAIGN_CREATION_FAILED   — POST request failed
    //   UPDATE_PAYLOAD_MISSING     — no payload provided in update mode
    console.error(`[${code}] ${message}`);
});

Campaign Field Reference

Use these API field keys in data-display-attr, data-callback-attr, data-update-payload, and the fields object of data-creation-payload. Dates are rendered using the browser's locale via Intl.DateTimeFormat.

API Field Key Default Display Label Notes
campaignnameCampaign Name
campaignstartdateStart DateYYYY-MM-DD, rendered via Intl.DateTimeFormat
campaignenddateEnd DateYYYY-MM-DD, rendered via Intl.DateTimeFormat
statusStatusShows if missing
clientClient
debtorDebtor
campaignbudgetBudget
currencyCurrency
permalink(link)Rendered as an Open in Wrike ↗ button in the header; not shown as a row
brandbrandNo default label; use label mapping
agencyagencyNo default label; use label mapping
campaignobjectivecampaignobjectiveNo default label; use label mapping
selectedchannelsselectedchannelsArray of strings; no default label
ℹ️
Any API field key not in the default label map is displayed using the raw key name as the label. Override any label using custom label mapping.

Dynamic Payloads (JavaScript API)

For the update mode, the update payload can be set or changed at any time by the host application — before or after the widget is initialized. This is the recommended approach when the fields to update depend on user interactions elsewhere on the page (e.g. a form).

Pattern

Set data-wmc-update-payload as a JSON string on the target <div>. The widget reads this attribute at the moment the "Update to Wrike" button is clicked.

example — dynamic-update.html
<!-- Widget initialized without a static payload -->
<div id="my-campaign"></div>
<script
    src="./wrike-media-connect.js"
    data-target="my-campaign"
    data-mode="update"
    data-campaign-id="MQAAAAEH0egx"
    data-display-attr='["campaignname","campaignbudget","client"]'
></script>

<!-- Host application form -->
<input id="new-budget" type="text" placeholder="New budget" />
<input id="new-name"   type="text" placeholder="New campaign name" />

<script>
    // Sync form values to the widget's dynamic payload before the user clicks
    function syncPayload() {
        const payload = {
            campaignbudget: document.getElementById('new-budget').value,
            campaignname:   document.getElementById('new-name').value
        };
        document.getElementById('my-campaign').dataset.wmcUpdatePayload =
            JSON.stringify(payload);
    }

    document.getElementById('new-budget').addEventListener('input', syncPayload);
    document.getElementById('new-name').addEventListener('input', syncPayload);
</script>
⚠️
Payload resolution order: If both data-wmc-update-payload (on the container div) and data-update-payload (on the script tag) are present, the runtime value wins. To fall back to the static payload, remove or clear the data-wmc-update-payload attribute.

Multiple Widgets on One Page

You can place any number of widget instances on the same page. Each instance is independent: it has its own Shadow DOM, its own data fetch, and fires events only on its own target element.

💡
All instances share the same OAuth token. Authenticating once authenticates all widgets on the page — and all widgets in other tabs of the same origin.
example — multi-instance.html
<!-- Campaign A: Display mode -->
<div id="campaign-a"></div>
<script src="./wrike-media-connect.js"
    data-target="campaign-a"
    data-campaign-id="IEAC7PRTI5SVIV6V"
    data-color-primary="#1E40AF"
></script>

<!-- Campaign B: Update mode with different styling -->
<div id="campaign-b"></div>
<script src="./wrike-media-connect.js"
    data-target="campaign-b"
    data-mode="update"
    data-campaign-id="MQAAAAEH0egx"
    data-color-primary="#14532d"
    data-color-accent="#16a34a"
></script>

<script>
    // Each element receives only its own events
    document.getElementById('campaign-a').addEventListener('wrike-media-connect:data', (e) => {
        console.log('Campaign A data:', e.detail);
    });

    document.getElementById('campaign-b').addEventListener('wrike-media-connect:updated', (e) => {
        console.log('Campaign B was updated:', e.detail);
    });
</script>

Theming & Colors

Four color attributes control the widget's appearance. All accept any valid CSS color value (hex, rgb(), named colors, etc.). Colors are injected into the Shadow DOM stylesheet at initialization time.

AttributeWhere AppliedDefault
data-color-primary Header background #1E40AF (dark blue)
data-color-background Widget body background #FFFFFF (white)
data-color-text Body text, field values #111827 (near-black)
data-color-accent Buttons, links #3B82F6 (blue)
<!-- Dark theme example -->
<script src="./wrike-media-connect.js"
    data-target="dark-widget"
    data-campaign-id="IEAC7PRTI5SVIV6V"
    data-color-primary="#111827"
    data-color-background="#1f2937"
    data-color-text="#f9fafb"
    data-color-accent="#60a5fa"
></script>

Custom Label Mapping

By default, field keys without a built-in label (e.g. brand, agency) are shown using their raw API key name. You can override any label — including defaults — by storing a mapping array in localStorage under wmc_cfg_label_map.

This is typically configured by a Tech Admin once for all widgets across the host application.

// Set custom labels for any field key
localStorage.setItem('wmc_cfg_label_map', JSON.stringify([
    { key: 'campaignname',    label: 'Campaign'        },
    { key: 'campaignbudget',  label: 'Budget (USD)'    },
    { key: 'brand',           label: 'Brand'           },
    { key: 'agency',          label: 'Agency'          },
    { key: 'requestormarket', label: 'Market'          },
    { key: 'cssid',           label: 'CSS ID'          }
]));
// Changes take effect on the next widget initialization (page load).
ℹ️
Custom labels are merged with the defaults. Custom entries take precedence. If a key's label is an empty string, the raw API key is used as the fallback.

Configuration Keys (localStorage)

The widget reads several localStorage keys at initialization. These are typically set by a Tech Admin configuration page, not by the host application developer.

Key Default Description
wmc_oauth_token none The stored Bearer token. Written by callback.html after OAuth. Do not set manually.
wmc_cfg_xpi_base_url https://xpi-api.gowrike.space/ Base URL for all XPI API requests. Must include a trailing slash.
wmc_cfg_xpi_client_id MediaConnectWidget OAuth client identifier sent during the authorization and token exchange flows.
wmc_cfg_label_map none JSON array of {"key","label"} objects. See Label Mapping.

Authentication & OAuth

The widget uses the OAuth 2.0 Authorization Code flow, handled entirely in the browser. No server-side code is required from the host application.

Flow walkthrough

  1. 1

    Check localStorage. On initialization, the widget reads wmc_oauth_token. If a token exists, it proceeds directly to fetching campaign data.

  2. 2

    Show login prompt. If no token is found, the widget renders a "Connect to Wrike" button (label varies by mode).

  3. 3

    Open OAuth popup. On click, a 600×700 popup window opens to the XPI authorization endpoint. The URL encodes baseUrl and clientId in the state parameter (base64-encoded JSON) so they survive the redirect.

  4. 4

    User grants access. The XPI authorization server redirects the popup to callback.html?code=XXXXX.

  5. 5

    Token exchange. callback.html extracts the authorization code, calls the XPI token exchange endpoint, and receives a Bearer token.

  6. 6

    Token stored & broadcast. The token is written to localStorage. callback.html sends { type: 'wmc_auth_success', token } via postMessage to the opener. The widget also polls localStorage every second as a fallback for cross-origin popups where postMessage may be blocked.

  7. 7

    Widget re-renders. On receiving the token, the widget broadcasts wmc_token_changed via BroadcastChannel, causing every widget instance on the page (and in other same-origin tabs) to re-render immediately.

  8. 8

    Session expiry. If any API call returns HTTP 401, the widget renders the login prompt again. The token is not removed automatically — the user must click "Connect to Wrike" to re-authenticate. Clicking logout removes the token from localStorage and resets all instances.

Simulating logout / token expiry

// Force all widgets back to the login state (for testing)
localStorage.removeItem('wmc_oauth_token');
location.reload(); // or wait for BroadcastChannel to propagate

Wrike Media Connect — Developer Documentation  ·  Last updated 2026