Ember API

A small, CORS-open REST surface so you can list past issues and sign people up to your newsletter from anywhere — your website, your docs site, your app's footer.

Basics

Base URL

https://ember.mminnovativetech.com

Authentication

None. The endpoints mirror the public web surface of a newsletter.

Two independent archive toggles

The newsletter owner has two separate switches on their settings page:

The two are independent: an author can keep their archive embeddable on a third-party site (web off, API on), or hide it from machines while keeping the human archive live (web on, API off). Signup endpoints — web and API — always work.

Content types

All responses are application/json; charset=utf-8. POST requests accept either application/json or application/x-www-form-urlencoded.

Newsletter slug

Every newsletter has a URL slug visible to its owner on the Ember dashboard — it appears in the public archive URL (https://ember.mminnovativetech.com/n/<slug>) and in every API path. Slugs are stable; they never change after the newsletter is created.

List past issues

GET /api/n/{slug}/issues

Returns every sent issue for the newsletter, newest first. Drafts and scheduled issues are never exposed.

Request

GET https://ember.mminnovativetech.com/api/n/weekly-updates/issues

Response — 200 OK

{
  "ok": true,
  "newsletter": {
    "slug": "weekly-updates",
    "name": "Weekly Updates",
    "description": "What we shipped this week."
  },
  "issues": [
    {
      "slug": "shipping-the-new-editor",
      "subject": "Shipping the new editor",
      "preheader": "Plus three bug fixes you asked for.",
      "sent_at": 1779600000,
      "url": "https://ember.mminnovativetech.com/n/weekly-updates/shipping-the-new-editor"
    },
    {
      "slug": "first-issue",
      "subject": "Hello, world",
      "preheader": "We're starting a newsletter.",
      "sent_at": 1778995200,
      "url": "https://ember.mminnovativetech.com/n/weekly-updates/first-issue"
    }
  ]
}

Fields

FieldTypeDescription
newsletter.slug string Stable URL slug for the newsletter.
newsletter.name string Display name shown in emails and the public archive.
newsletter.description string Short tagline. May be empty.
issues[].slug string Stable URL slug for the issue.
issues[].subject string Email subject line.
issues[].preheader string Preview text shown next to the subject in most clients. May be empty.
issues[].sent_at integer Unix epoch seconds (UTC) at which the issue went out.
issues[].url string Canonical public URL of the issue inside Ember's archive.

Response — 404 Not Found

Either the newsletter slug doesn't exist, or the owner has turned off the API archive toggle:

{ "ok": false, "error": "not_found" }
{ "ok": false, "error": "archive_disabled" }

archive_disabled here refers specifically to the API toggle. The web archive being off does not affect this endpoint.

Subscribe an email

POST /api/n/{slug}/subscribe

Adds an email to the newsletter as pending and triggers Ember's standard double opt-in confirmation email. The address only becomes a real subscriber once they click the link in that email.

Request — JSON

POST https://ember.mminnovativetech.com/api/n/weekly-updates/subscribe
Content-Type: application/json

{ "email": "alice@example.com" }

Request — form-encoded

POST https://ember.mminnovativetech.com/api/n/weekly-updates/subscribe
Content-Type: application/x-www-form-urlencoded

email=alice%40example.com

Response — 200 OK

{ "ok": true, "message": "Check your inbox to confirm your subscription." }

The same 200 shape is returned for a brand-new signup, a re-signup of an unconfirmed address (the confirm email is re-sent with a fresh token), and an address that's already a confirmed subscriber. This is intentional — the endpoint must not be a way to test whether a given email is on someone's list.

Response — 400 Bad Request

{ "ok": false, "error": "invalid_email" }

Response — 404 Not Found

{ "ok": false, "error": "not_found" }

CORS & preflight

Both endpoints send permissive CORS headers so they can be called from JavaScript on any origin:

Access-Control-Allow-Origin:   *
Access-Control-Allow-Methods:  GET, POST, OPTIONS
Access-Control-Allow-Headers:  Content-Type
Access-Control-Max-Age:        86400
Vary:                          Origin

A browser will issue an OPTIONS preflight before a JSON POST. The endpoint responds with 204 No Content and the headers above:

OPTIONS /api/n/{slug}/subscribe

No cookies are read or set on the API surface, so credentials never bleed across origins.

Errors

StatusError codeMeaning
200 Success. ok is always true.
204 OPTIONS preflight. Empty body.
400invalid_email The email failed format validation.
404not_found No newsletter at that slug.
404archive_disabled The newsletter exists but its owner turned off the public archive.

Rate limiting

No per-IP rate limit. The signup endpoint is idempotent: re-submitting the same address rotates the confirm token but doesn't duplicate the subscription. If you publish the subscribe call on a high-traffic page and start seeing abuse, talk to us — we can add a per-newsletter quota.

Drop-in embed snippet

The smallest possible signup form for your own website. Replace weekly-updates with your newsletter's slug.

<form id="ember-signup">
  <input type="email" required placeholder="you@example.com">
  <button>Subscribe</button>
</form>

<script>
document.getElementById('ember-signup').addEventListener('submit', async (e) => {
  e.preventDefault();
  const input = e.target.querySelector('input');
  const res = await fetch(
    'https://ember.mminnovativetech.com/api/n/weekly-updates/subscribe',
    {
      method:  'POST',
      headers: { 'Content-Type': 'application/json' },
      body:    JSON.stringify({ email: input.value }),
    }
  );
  const data = await res.json();
  if (data.ok) {
    e.target.innerHTML =
      '<p>Check your inbox — we just sent a confirmation link.</p>';
  } else {
    alert(data.error || 'Something went wrong, please try again.');
  }
});
</script>

That's it — no SDK, no build step, no auth. The same applies to fetching past issues for an archive widget elsewhere on your site.

Ready to wire it up?

You'll need an active Ember subscription and a configured newsletter.

Go to Ember