> For clean Markdown of any page, append .md to the page URL. > For a complete documentation index, see https://mailchimp.com/developer/marketing/build/start-developing/set-up-an-oauth-app/llms.txt. > For AI client integration (Claude Code, Cursor, etc.), connect to the MCP server at https://mailchimp.com/_mcp/server. # Set up an OAuth app > Register an application, implement the OAuth 2 flow on your server, and rotate a client secret. This page walks through implementing OAuth 2 so your application can access the Marketing API on behalf of other Mailchimp users. For how the flow works and why it has the steps it does, see [OAuth 2](/marketing/api-concepts/authentication/oauth-2). ## What you'll need * A Mailchimp account * [An API key](/marketing/build/get-started/generate-your-api-key), if you want to test against your own account first * A **redirect URI** for your application, the URL Mailchimp sends users back to after they authorize your application. It must be reachable by the user's browser, but for local development `http://127.0.0.1:3000/...` is fine. ## Register your application Registering gives you the `client_id` and `client_secret` your server needs. 1. Navigate to the [**Registered Apps page**](https://us1.admin.mailchimp.com/account/oauth2/) in your Mailchimp account 2. Click **Register An App** 3. Fill out the **Register An App** form 4. Click **Create** You'll see the `client_id` and `client_secret` at the bottom of the page. > **Note** > > This is the only time you'll be able to see `client_secret`, so you'll need to > copy it and store it securely. ## Implement the flow on your server The sample below handles the whole flow: authenticating the user, exchanging the code for an access token, then making a first authenticated request on their behalf. Pick your language. **`TypeScript`** ```typescript title="TypeScript" const express = require("express"); const querystring = require("querystring"); const bodyParser = require("body-parser"); const fetch = require("node-fetch"); const { URLSearchParams } = require("url"); const { MailchimpClient } = require("@mailchimp/mailchimp-marketing"); // Basic express app setup const app = express(); app.use(bodyParser.json()); app.use( bodyParser.urlencoded({ extended: true }) ); // You should always store your client id and secret in environment variables for security — the exception: sample code. const MAILCHIMP_CLIENT_ID = "YOUR_CLIENT_ID"; const MAILCHIMP_CLIENT_SECRET = "YOUR_CLIENT_SECRET"; const BASE_URL = "http://127.0.0.1:3000"; const OAUTH_CALLBACK = `${BASE_URL}/oauth/mailchimp/callback`; // 1. Navigate to http://127.0.0.1:3000 and click Login app.get("/", function(req, res) { res.send( '

Welcome to the sample Mailchimp OAuth app! Click here to log in

' ); }); // 2. The login link above will direct the user here, which will redirect // to Mailchimp's OAuth login page. app.get("/auth/mailchimp", (req, res) => { res.redirect( `https://login.mailchimp.com/oauth2/authorize?${querystring.stringify({ response_type: "code", client_id: MAILCHIMP_CLIENT_ID, redirect_uri: OAUTH_CALLBACK })}` ); }); // 3. Once // 3. Once the user authorizes your app, Mailchimp will redirect the user to // this endpoint, along with a code you can use to exchange for the user's // access token. app.get("/oauth/mailchimp/callback", async (req, res) => { const { query: { code } } = req; // Here we're exchanging the temporary code for the user's access token. const tokenResponse = await fetch( "https://login.mailchimp.com/oauth2/token", { method: "POST", body: new URLSearchParams({ grant_type: "authorization_code", client_id: MAILCHIMP_CLIENT_ID, client_secret: MAILCHIMP_CLIENT_SECRET, redirect_uri: OAUTH_CALLBACK, code }) } ); const { access_token } = await tokenResponse.json(); console.log(access_token); // Below, we're using the access token to make an authenticated request on // behalf of the user who just granted OAuth access. You wouldn't keep this // in your production code, but it's here to demonstrate how the call is // made. const mailchimp = new MailchimpClient({ token: access_token }); const response = await mailchimp.ping.list(); console.log(response); res.send(`

This user's access token is ${access_token}.

When pinging the Mailchimp Marketing API's ping endpoint, the server responded:

${response} `); // In reality, you'd want to store the access token somewhere in your // application. // fakeDB.getCurrentUser(); // fakeDB.storeMailchimpCredsForUser(user, { // access_token // }); }); app.listen(3000, "127.0.0.1", function() { console.log( "Server running on port 3000; visit http://127.0.0.1:3000" ); }); ``` **`PHP`** ```php title="PHP" Welcome to the sample Mailchimp OAuth app! Click here to log in

'; } // 2. The login link above will direct the user here, which will redirect // to Mailchimp's OAuth login page. if (isset($_GET['login'])) { header('Location: https://login.mailchimp.com/oauth2/authorize?'.http_build_query([ 'response_type' => 'code', 'client_id' => $mailchimp_client_id, 'redirect_uri' => $oauth_callback, ])); exit(); } // 3. Once the user authorizes your app, Mailchimp will redirect the user to // this endpoint, along with a code you can use to exchange for the user's // access token. if (isset($_GET['code'])) { $url = 'https://login.mailchimp.com/oauth2/token'; $context = stream_context_create([ 'http' => [ 'header' => "Content-type: application/x-www-form-urlencoded\r\n", 'method' => 'POST', 'content' => http_build_query([ 'grant_type' => "authorization_code", 'client_id' => $mailchimp_client_id, 'client_secret' => $mailchimp_client_secret, 'redirect_uri' => $oauth_callback, 'code' => $_GET['code'], ]), ], ]); $result = file_get_contents($url, false, $context); $decoded = json_decode($result); $access_token = $decoded->access_token; // Below, we're using the access token to make an authenticated request on // behalf of the user who just granted OAuth access. You wouldn't keep this // in your production code, but it's here to demonstrate how the call is // made. $mailchimp = new MailchimpClient($access_token); try { $response = $mailchimp->ping->list(); echo "

This user\'s access token is $access_token.

"; echo "

When pinging the Mailchimp Marketing API\'s ping endpoint, the server responded:

"; echo "" . json_encode($response) . ""; } catch (exception $e) { var_dump($e); } // In reality, you'd want to store the access token somewhere in your // application. // fakeDB.getCurrentUser(); // fakeDB.storeMailchimpCredsForUser(user, { // access_token // }); } ``` **`Ruby`** ```ruby title="Ruby" require 'mailchimp' require 'sinatra' require 'net/http' MAILCHIMP_CLIENT_ID = ENV['MAILCHIMP_CLIENT_ID'] MAILCHIMP_CLIENT_SECRET = ENV['MAILCHIMP_CLIENT_SECRET'] BASE_URL = "http://127.0.0.1:3000" OAUTH_CALLBACK = "#{BASE_URL}/oauth/mailchimp/callback" # Basic Sinatra setup configure do set :port, 3000 end # 1. Navigate to http://127.0.0.1:3000 and click Login get '/' do "

Welcome to the sample Mailchimp OAuth app! Click here to log in

" end # 2. The login link above will direct the user here, which will redirect # to Mailchimp's OAuth login page get '/auth/mailchimp' do query_params = "response_type=code&client_id=#{MAILCHIMP_CLIENT_ID}&redirect_uri=#{OAUTH_CALLBACK}" redirect to("https://login.mailchimp.com/oauth2/authorize?#{query_params}") end # 3. Once the user authorizes your app, Mailchimp will redirect the user to # this endpoint, along with a code you can use to exchange for the user's # access token. get '/oauth/mailchimp/callback' do code = params['code'] body = "grant_type=authorization_code&client_id=#{MAILCHIMP_CLIENT_ID}&client_secret=#{MAILCHIMP_CLIENT_SECRET}&redirect_uri=#{OAUTH_CALLBACK}&code=#{code}" uri = URI("https://login.mailchimp.com/oauth2/token") response = JSON.parse(Net::HTTP.post(uri, body).body) access_token = response['access_token'] mailchimp = Mailchimp::Client.new(token: access_token) mailchimp_res = mailchimp.ping.list """

This user's access token is #{access_token}.

When pinging the Mailchimp Marketing API's ping endpoint, the server responded:

#{mailchimp_res} """ # In reality, you'd want to store the access token somewhere in your # application. # user = fake_db.get_current_user # fake_db.store_mailchimp_creds_for_user(user, { access_token: access_token }); end ``` **`Python`** ```python title="Python" #!/usr/bin/env python from urllib.parse import urlencode from operator import itemgetter import json import requests from flask import Flask, redirect, request from mailchimp_marketing import MailchimpClient BASE_URL = "http://127.0.0.1:5000" OAUTH_CALLBACK = "{}/oauth/mailchimp/callback".format(BASE_URL) app = Flask(__name__) @app.route('/') def index(): return "

Welcome to the sample Mailchimp OAuth app! Click here to log in

" # 2. The login link above will direct the user here, which will redirect # to Mailchimp's OAuth login page. @app.route('/auth/mailchimp', methods=['GET']) def auth(): # You should always store your client id and secret in environment variables for security — the exception: sample code. MAILCHIMP_CLIENT_ID = "YOUR_CLIENT_ID" oauth_base = "https://login.mailchimp.com/oauth2/authorize?" return redirect("{}{}".format(oauth_base, urlencode({ "response_type": "code", "client_id": MAILCHIMP_CLIENT_ID, "redirect_uri": OAUTH_CALLBACK }))) # 3. Once the user authorizes your app, Mailchimp will redirect the user to # this endpoint, along with a code you can use to exchange for the user's # access token. @app.route('/oauth/mailchimp/callback', methods=['GET']) def authCallback(): # You should always store your client id and secret in environment variables for security — the exception: sample code. MAILCHIMP_CLIENT_ID = "YOUR_CLIENT_ID" MAILCHIMP_CLIENT_SECRET = "YOUR_CLIENT_SECRET" code = request.args['code'] app.logger.info('requesting token using %s code' % code) # Here we're exchanging the temporary code for the user's access token. tokenResponse = requests.post("https://login.mailchimp.com/oauth2/token", data={ 'grant_type': 'authorization_code', 'client_id': MAILCHIMP_CLIENT_ID, 'client_secret': MAILCHIMP_CLIENT_SECRET, 'redirect_uri': OAUTH_CALLBACK, 'code': code }) app.logger.info('status_code: %s' % tokenResponse.status_code) app.logger.info('response: %s' % tokenResponse.json()) access_token = itemgetter('access_token')(tokenResponse.json()) # Below, we're using the access token to make an authenticated request on # behalf of the user who just granted OAuth access. You wouldn't keep this # in your production code, but it's here to demonstrate how the call is # made. mailchimp = MailchimpClient(token=access_token) response = mailchimp.ping.list() app.logger.info('Ping response: %s' % json.dumps(response)) return """

This user's access token is {}.

When pinging the Mailchimp Marketing API's ping endpoint, the server responded:

{} """.format(access_token, code) # In reality, you'd want to store the access token somewhere in your # application. # fakeDB.getCurrentUser() # fakeDB.storeMailchimpCredsForUser(user, { # access_token # }) if __name__ == '__main__': app.run(debug=True, host='0.0.0.0') ``` The code above completes the flow and produces an access token, then uses it to call the [Ping endpoint](/marketing/api/ping/list), a trivial request that confirms the token works. It doesn't persist anything; in a real application you'd store the access token against your own user record. The two URLs involved, if you're implementing this without the sample: * Redirect the user to `https://login.mailchimp.com/oauth2/authorize?response_type=code&client_id=YOUR_CLIENT_ID&redirect_uri=YOUR_REDIRECT_URI` * Exchange the returned `code` with a POST to `https://login.mailchimp.com/oauth2/token` ## Rotate a client secret If your `client_secret` is exposed, rotate it: 1. Navigate to the [**Registered Apps page**](https://us1.admin.mailchimp.com/account/oauth2/) in your Mailchimp account. 2. Click **Edit** for the registered app you need to update. 3. In the **Client secret** field, click **Rotate**. 4. On the pop-up modal, type **ROTATE** and click **Rotate Client Secret**. You'll see a modal with the `client_id` and the updated `client_secret`. Update your application with the new secret; once rotation completes, requests signed with the old one fail with a `401`. > **Note** > > This is the only time you'll be able to see the updated `client_secret`, so > you'll need to copy it and store it securely. ## Next steps * [API structure](/marketing/api-concepts/api-structure) — conventions you'll hit across the API * [Batch operations](/marketing/api-concepts/batch-operations) — syncing contacts in bulk once you have a token * [Webhooks](/marketing/api-concepts/webhooks) — keeping your platform in step with changes made in Mailchimp > Find all the Mailchimp API documentation and tools developers need to send marketing and transactional emails.