I want to use Ajax to POST to a plugin endpoint and get a JSON response. The plugin I'm using is for Mailchimp newsletter subscriptions and should allow for JSON responses:


I have my form:

<form action="" method="post" novalidate>
  {{ csrfInput() }}
  <input id="email" name="email" type="email" >
  <input type="submit" value="Subscribe">

and my Javascript:

let json = this.toJSONString(this.form);
let xhr = new XMLHttpRequest();
xhr.onload = () => {
  if (xhr.status >= 200 && xhr.status < 300) {
    // ...
  } else {
     // ...
xhr.open("POST", "actions/mailchimp-subscribe/audience/subscribe", true);
xhr.setRequestHeader("Content-Type", "application/json; charset=UTF-8");

This hits the endpoint, but I get a 404 response:

HTTP 404 – Not Found – yii\web\NotFoundHttpException

Template not found: actions/mailchimp-subscribe/audience/subscribe

As I understand it, I'm correctly posting to the relevent plugin controller action by using the URL actions/mailchimp-subscribe/audience/subscribe. Furthermore, if I do a GET to that URL I get a 400 response which would lead me to believe the routing is correct.

The error therefore seems to be in the Craft controller/routing handler. It's not detecting that I've send a JSON string and that I want a JSON response (and not a template/HTML response).

What's going on here?

2 Answers 2


In your newsletter form, you should have:

<input type="hidden" name="action" value="mailchimp-subscribe/audience/subscribe">
<input type="hidden" name="language" value="{{ craft.app.locale.id }}">

Then, you should POST to '/' + userLocale (where userLocale = $('[name="language"]').val()).

I am using jQuery, and this is working fine:

    url: '/' + userLocale,
    dataType: 'json',
    data: $('#form').serialize(),
    success: function(response) {
    error: function(response) {
  • Thanks but I'm trying to do it without jQuery as vanilla JS. Also I don't have any localization. I've tried posting to the root URL with the included hidden inputs but it still throws errors Mar 2, 2020 at 16:39
  • To the root URL, you mean '/'? Maybe you can also add this to the <head> tag: <base href="{{ alias('@web/') }}">.
    – Romain P.
    Mar 2, 2020 at 16:42

The problem here was I hadn't included the Accept header

xhr.setRequestHeader("Accept", "application/json");

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.