# Mailchimp OAuth

Connect Mailchimp to manage audiences, campaigns, and automations.

JsWorkflows provides a platform-managed Mailchimp OAuth app. You do not need to create a Mailchimp app or provide credentials.

## Available scopes

Mailchimp OAuth grants full account access. There are no granular scope selections. All connected resources are available once you authorize.

## Connecting

1. Go to **OAuth Connections → Add Connection → Mailchimp**.
2. Click through to the Mailchimp authorisation screen.
3. Log in and click **Allow**.
4. Give the connection a handle (e.g., `my-mailchimp`).

Use lowercase letters, numbers, and hyphens only for the handle.

Mailchimp tokens do not expire and are not automatically refreshed.

## Finding your API endpoint and data centre

Mailchimp routes API requests through a data-centre-specific base URL (e.g., `https://us1.api.mailchimp.com`). After connecting, call the metadata endpoint to retrieve it:

```js
const { token, error } = await api.getOAuthToken('my-mailchimp');
if (error || !token) throw new Error(error || 'Missing Mailchimp token');

const metaResponse = await fetch('https://login.mailchimp.com/oauth2/metadata', {
  headers: { 'Authorization': `OAuth ${token}` },
});

if (!metaResponse.ok) {
  throw new Error(`Mailchimp metadata ${metaResponse.status}: ${await metaResponse.text()}`);
}

const meta = await metaResponse.json();

const apiBase = meta.api_endpoint; // e.g. "https://us1.api.mailchimp.com"
```

## Example: add a subscriber to an audience

```js
export class Workflow {
  async start(data, _headers, api) {
    await api.scheduleNextStep({
      delay: 10,
      action: 'subscribe',
      payload: {
        email: data.email,
        firstName: data.customer?.first_name,
        lastName: data.customer?.last_name,
      },
    });
  }

  async subscribe({ email, firstName, lastName }, _headers, api) {
    const { token, error } = await api.getOAuthToken('my-mailchimp');
    if (error || !token) throw new Error(error || 'Missing Mailchimp token');

    const metaResponse = await fetch('https://login.mailchimp.com/oauth2/metadata', {
      headers: { 'Authorization': `OAuth ${token}` },
    });

    if (!metaResponse.ok) {
      throw new Error(`Mailchimp metadata ${metaResponse.status}: ${await metaResponse.text()}`);
    }

    const meta = await metaResponse.json();

    const listId = env.MAILCHIMP_LIST_ID;
    const response = await fetch(`${meta.api_endpoint}/3.0/lists/${listId}/members`, {
      method: 'POST',
      headers: {
        'Content-Type': 'application/json; charset=utf-8',
        Authorization: `Bearer ${token}`,
      },
      body: JSON.stringify({
        email_address: email,
        status: 'subscribed',
        merge_fields: { FNAME: firstName || '', LNAME: lastName || '' },
      }),
    });

    if (!response.ok) {
      throw new Error(`Mailchimp API ${response.status}: ${await response.text()}`);
    }

    const member = await response.json();
    console.log(`Subscribed ${member.email_address}`);
  }
}
```

`MAILCHIMP_LIST_ID` should be the audience ID. `merge_fields` keys such as `FNAME` and `LNAME` must match merge tags that already exist on that audience.