# Welcome

Welcome to the MemberSpace Developer documentation! Learn about our API and how you can use it to expand your site's capabilities.

*<mark style="background-color:red;">This documentation is only intended for those familiar with Javascript.</mark>*

## Javascript API

Add custom membership logic to your website frontend.

{% content-ref url="/pages/-MYRIwx\_vzaJ0tJhffJk" %}
[Signature](/javascript-api/overview)
{% endcontent-ref %}


# Install Code

In order for MemberSpace to work on your website you will have to include our install code which can also be found in the MemberSpace Admin.

```javascript
var MemberSpace = window.MemberSpace || {subdomain: "YOUR_SUBDOMAIN"};
(function(d){
  var s = d.createElement("script");
  s.src = "https://cdn.memberspace.com/scripts/widgets.js";
  var e = d.getElementsByTagName("script")[0];
  e.parentNode.insertBefore(s,e);
}(document));
```

{% hint style="info" %}
Make sure to replace `YOUR_SUBDOMAIN` with the subdomain form your account.
{% endhint %}


# Multi-subdomain site

In case your site spreads multiple subdomains, like:

* yoursite.com (or [www.yoursite.com](http://www.yoursite.com))
* blog.yoursite.com
* docs.yoursite.com
* etc.

you can tie these subdomains together. This means if a member logs in/out at any of the subdomains, they will be logged in/out at all of them.<br>

{% hint style="success" %}
The MemberSpace widget normally uses LocalStorage to persist information across visits / refreshes. While LocalStorage is the preferred way of storing things, it cannot be accessed in a cross-subdomain manner.&#x20;

For this reason, if the above feature is in use, the widget persists authentication data (a subset of all persisted information) in Cookies instead.
{% endhint %}

{% hint style="warning" %}
While Cookies can be accessed in a cross-subdomain manner, because of privacy and tracking prevention reasons, browsers regularly introduce new limitations and blocks on them.

For this reason, this multi-subdomain login feature can stop working for a subset of browsers at any time and without prior notice. (I.e., members on the latest Chrome / Safari / / Edge / Firefox version not being able to log in)
{% endhint %}

To enable this feature you have to edit the configuration part of your install code as follows

```javascript
var MemberSpace = window.MemberSpace || {
  subdomain: "YOUR_SUBDOMAIN",
  cookieDomain: "BASE_DOMAIN"
};
```

{% hint style="info" %}
The value of `cookieDomain` has to be the top level domain that you own. In the above example this would be `yoursite.com`\
\
Make sure that you replace `YOUR_SUBDOMAIN` and `BASE_DOMAIN` with the appropriate values.

\
Make sure that all instances of the install code are updated across all subdomains of the site you want to connect together.
{% endhint %}

{% hint style="warning" %}
Any time you change the `cookieDomain` value, all users currently logged in to the site may be logged out, requiring them to log back in.
{% endhint %}


# Using query parameters

The widget is capable of opening a modal with a specific screen on startup when instructed using a query parameter

{% hint style="info" %}
You can use the `msopen` query parameter on any page the widget is installed on.\
\
Path sections starting with `:` are placeholders, values need to be substituted in in their place. Placeholders, ending with `?` are optional and can be omitted\
\
I.e., given a domain of \```https://example.com`, a`` content id of \`abc123\` and a path of\
`/content/:contentId/:viewingPreference?`\
\
`the following URLs can be generated:`\
`- https://example.com?msopen=/content/abc123`\
`- https://example.com?msopen=/content/abc123/popup`
{% endhint %}

### Recognized paths

<table data-full-width="true"><thead><tr><th width="148.33333333333331">Target</th><th>path</th><th>If logged in</th><th>If not logged in</th></tr></thead><tbody><tr><td>Log in</td><td>/member/sign_in</td><td>Will take to the <code>member menu</code> or if not enabled, the <code>account screen</code></td><td>Show login screen</td></tr><tr><td>Log out</td><td>/member/sign_out</td><td>Log user out</td><td>-</td></tr><tr><td>Register user</td><td>/member/sign_up/:planPublicId?</td><td>Will take to the <code>member menu</code> or if not enabled, the <code>account screen</code>. Will take the</td><td>Will register the user and offer to sign up to a plan.<br>- If no plan specified, the <code>after registration action</code> will be executed.</td></tr><tr><td>Sign user up to a plan</td><td>/member/plans/:planPublicId?</td><td>Will offer to sign up to a plan.<br>- If no plan specified, the <code>after registration action</code> will be executed.<br>- If user is already on specified plan, will navigate browser to the content URL of the plan.</td><td>Register user first</td></tr><tr><td>All plans sign up</td><td>/member/plans/all</td><td>Will offer to sign up to any public plan</td><td>Register user first</td></tr><tr><td>Account</td><td>/member/account</td><td>Display account screen</td><td>Log user in first</td></tr><tr><td>Account edit</td><td>/member/account/edit</td><td>Display account edit screen</td><td>Log user in first</td></tr><tr><td>Email change</td><td>/member/account/email_change</td><td>Display email change screen</td><td>Log user in first</td></tr><tr><td>Password change</td><td>/member/account/password_change</td><td>Display password change screen</td><td>Log user in first</td></tr><tr><td>Invoices</td><td>/member/invoices</td><td>Display invoice list screen</td><td>Log user in first</td></tr><tr><td>Payment method change</td><td>/member/payment_method/update</td><td>Display payment method change screen</td><td>Log user in first</td></tr><tr><td>Membership list</td><td>/member/memberships</td><td>Display membership list screen</td><td>Log user in first</td></tr><tr><td>Protected content</td><td>/content/:contentId/:viewingPreference?</td><td>Display protected content.<br><code>viewingPreference</code> can be <code>download</code> (default), or <code>popup</code>. Some content types only support a specific `viewingPreference`</td><td>Log user in first</td></tr></tbody></table>


# Signature

The MemberSpace widget provides a variety of tools in order to achieve better integration between the host site and MemberSpace.

## Resources

{% content-ref url="/pages/-MYRKVjSSeJnn2wyphqh" %}
[Events](/javascript-api/overview/events)
{% endcontent-ref %}

{% content-ref url="/pages/-MYRO1g0QhV0p1QTMV2x" %}
[Functions](/javascript-api/overview/functions)
{% endcontent-ref %}

{% content-ref url="/pages/-MYRgt7k9I-8ooPFMiHC" %}
[Flags](/javascript-api/overview/flags)
{% endcontent-ref %}

{% content-ref url="/pages/-M\_1uxQyJ8KdAhtOOJ\_y" %}
[Structures](/javascript-api/overview/structures-and-examples)
{% endcontent-ref %}


# Events

You can listen for events and run custom code. We will dispatch a series of Custom Events based on lifecycle events and actions the member may take.

{% hint style="warning" %}
To eliminate any chance of [events](/javascript-api/overview/events) being fired before the appropriate listeners have been added, listeners should be registered before the [install code](/getting-started/install-code) is run. If this is not possible or impractical, please implement a code based on [this](/javascript-api/guides/member-info-spa#single-page-apps) guide.
{% endhint %}


# MemberSpace.ready

Dispatched only once, when widget becomes ready with a variety of information in the event detail. The `detail` field of the event contains [MemberInfo](/javascript-api/overview/structures-and-examples/structures) object if the member is logged in.

```javascript
const handleReady = ({ detail }) => {
  const { memberInfo } = detail;

  if (memberInfo) {
    console.log(memberInfo.email)
  }
}
document.addEventListener('MemberSpace.ready', handleReady);
```


# MemberSpace.member.info

Only dispatched if there is a logged in user:

* At bootstrap with cached user information, (re-dispatched when fresh information arrives and it is different from the cached)
* After the user logs in
* Re-dispatched every time some relevant information changes.

The `detail` field of the event contains a [MemberInfo](/javascript-api/overview/structures-and-examples/structures#memberinfo) object.

```javascript
const handleMemberInfo = ({ detail }) => {
    const { memberInfo } = detail;

    console.log(memberInfo.email)
}
document.addEventListener('MemberSpace.member.info', handleMemberInfo);
```


# MemberSpace.member.logout

Dispatched if there is no user logged in (at start) or if the user has just logged out.

```javascript
const handleLogout = () => {
    console.log('No user is logged in');
}
document.addEventListener('MemberSpace.member.logout', handleLogout);
```


# MemberSpace.member.registration

Dispatched when when a new member registers.

The `detail` field of the event contains a [MemberInfo](/javascript-api/overview/structures-and-examples/structures#memberinfo) object.

```javascript
const handleMemberRegistration = ({ detail }) => {
    const { memberInfo } = detail;
    
    console.log(memberInfo.email)
}
document.addEventListener('MemberSpace.member.registration', handleMemberRegistration);
```


# MemberSpace.conversion

Dispatched after a successful plan signup.

{% hint style="info" %}
This event will be fired for all plan types, including free plans.
{% endhint %}

```javascript
const handleConversion = ({ detail }) => {
  const { order, membership, member } = detail;

  console.log('order', order);
  console.log('membership', membership);
  console.log('member', member);
};

document.addEventListener('MemberSpace.conversion', handleConversion);
```

The `detail` object of the event contains the following structure:

```javascript
{
  membership: {
    id: 13579,
    planId: 151,
    publicPlanId: "abc123",
    name: "Super plan",
    // type [ 'free' | 'one_time_payment' | 'subscription' | 'multi_payment' ]
    type: "subscription",
    createdAt: "2021-03-24T15:31:55Z",
    status: "active",
    // expiresOn [ ISO-8601 | udnefined ] indicate that a **non-subscription** is
    // due to expire at a specific date.
    expiresOn: "2022-03-24T15:31:55Z"
    // cancelsOn [ ISO-8601 | undefined ] indicates that a recurring
    // **subscription** is being cancelled at the billing period end
    cancelsOn: "2022-03-24T15:31:55Z",
    // billingPeriodEnd [ ISO-8601 | udnefined ] indicates that when the next charge
    // on a **subscription or non-completed multi_payment** is due.
    billingPeriodEnd: "2021-06-24T15:31:55Z"
    paymentFailure: true,
    welcomeUrl: "/welcome-to-super-plan",
    contentUrl: "/super-plan-content"
  },
  order: {
    id: "string",
    subTotal: 12.35,
    subTotalCents: 1235,
    grandTotal: 12.35,
    grandTotalCents: 1235,
    // currency [ currency code | undefined ] available on non-free plans only
    currency: "usd"
  },
  member: {
    id: 678910,
    name: "John Doe",
    firstName: "John",
    lastName: "Doe",
    email: "john@doe.com",
    stripeCustomerId: "cus_xxxxxxxxxxxxxx",
  }
}
```


# Functions

{% hint style="warning" %}
These functions can be used anywhere in your custom code, but **you need to make sure the MemberSpace widget is loaded and ready**.

We suggest that you use them only from within an [event](/javascript-api/overview/events) handler or once the [MemberSpace.onReady flag](/javascript-api/overview/flags#memberspace-ready) is set. There is an [example code](/javascript-api/guides/member-info-spa#single-page-apps) with a promise interface that can help delay executing your code until the widget is ready.

In case of async functions, if an error occurs the promise will get rejected. Please make sure to handle promise rejection in your code!
{% endhint %}


# MemberSpace.getMemberInfo

A Synchronous function returns `isLoggedIn` and [MemberInfo](/javascript-api/overview/structures-and-examples/structures#memberinfo) (if logged in).

```javascript
const fn = () => {
  const data = MemberSpace.getMemberInfo();

  console.log(data);
  // either { isLoggedIn: true, memberInfo: {...} }
  // or     { isLoggedIn: false }
};
```


# MemberSpace.getMemberMetadata

An Asynchronous function (returns a Promise), which resolves with the member's metadata.

```javascript
// Using async-await
const fn = async () => {
    const metadata = await MemberSpace.getMemberMetadata();

    console.log(metadata);
};

// Using Promise chaining
MemberSpace
  .getMemberMetadata()
  .then(metadata => {
    console.log(metadata);
  });
```


# MemberSpace.updateMemberMetadata

An Asynchronous function (returns a Promise). It merges the data it gets as a parameter with the data stored in our servers as the member's metadata, and updates the stored information with the result.

```javascript
const dataToSet = {
    key1: 'value1',
    key2: {
        key3: 'value3'
    }
};

// Using async-await
const fn = async () => {
    await MemberSpace.updateMemberMetadata(dataToSet);

    // continue custom code
};

// Using Promise chaining
MemberSpace
  .updateMemberMetadata(dataToSet)
  .then(() => {
    // continue custom code
  });
```

{% hint style="info" %}
As a limitation, to enforce best practices the top level of the metadata must be an object.

The initial state of the metadata storage is `{}` (an empty object).

You can remove a key from any level by assigning `null` to it, but you cannot assign null as the content of the entire storage. To clear the storage please see [this](/javascript-api/overview/functions/memberspace.clearmembermetadata) function.
{% endhint %}


# MemberSpace.clearMemberMetadata

An Asynchronous function (returns a Promise). It resets the user's metadata storage to the default value: `{}` (empty object).

```javascript
// Using async-await
const fn = async () => {
    await MemberSpace.clearMemberMetadata();

    // continue custom code
};

// Using Promise chaining
MemberSpace
  .clearMemberMetadata()
  .then(() => {
    // continue custom code
  });
```


# Flags

Boolean indicators, used in order to verify the state of the widget

To provide information about its state, the widget maintains flags (boolean values) under the MemberSpace global object.


# MemberSpace.ready

Set to boolean `true` after the widget has been loaded

```javascript
if (MemberSpace.ready) {
  console.log("MemberSpace is ready!");
}
```


# Structures


# MemberInfo

This object contains information about the member, the plan(s) the member is on (memberships), and custom fields.

```javascript
{
  id: 115,
  name: "John Doe",
  firstName: "John",
  lastName: "Doe",
  email: "john.doe@testdomain.com",
  profileImageUrl: "https://memberspace.s3.us-east-1.amazonaws.com/store/xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx.jpg",
  memberships: [
    {
      id: 13579,
      planId: 151,
      publicPlanId: "abc123",
      name: "Super plan",
      type: "subscription",
      createdAt: "2021-03-24T15:31:55Z",
      status: "active",
      // cancelsOn [ ISO-8601 | undefined ] indicates that a recurring
      // **subscription** is being cancelled at the billing period end
      cancelsOn: "2022-03-24T15:31:55Z",
      // billingPeriodEnd [ ISO-8601 | udnefined ] indicates that when the next charge
      // on a **subscription or non-completed multi_payment** is due.
      billingPeriodEnd: "2022-03-24T15:31:55Z"
      paymentFailure: true,
      welcomeUrl: "/welcome-to-super-plan",
      contentUrl: "/super-plan-content"
    },
    {
      id: 13580,
      planId: 52,
      publicPlanId: "cde345",
      name: "Free plan",
      type: "free",
      createdAt: "2020-05-26T10:11:40Z",
      status: "active",
      // expiresOn [ ISO-8601 | udnefined ] indicate that a **non-subscription** is
      // due to expire at a specific date.
      expiresOn: "2022-03-24T15:31:55Z"
      paymentFailure: false,
      welcomeUrl: "/welcome-to-basic-plan",
      contentUrl: "/basic-plan-content"
    }
  ],
  customSignupFields: [
    {
      id: 21,
      type: "textarea",
      required: true,
      value: "Consectetur porro adipisci in possimus vero. Adipisci voluptas doloribus doloremque sint error id."
    },
    {
      id: 22,
      type: "text",
      required: true,
      value: "red"
    },
    {
      id: 23,
      type: "select",
      required: true,
      options: [
        { label: "D1", value: "D1" },
        { label: "D2", value: "D2" },
        { label: "D3", value: "D3" }
      ],
      value: "D1"
    },
    {
      id: 24,
      type: "multiple-select",
      required: true,
      options: [
        { label: "MO1", value: "MO1" },
        { label: "MO2", value: "MO2" },
        { label: "MO3", value: "MO3" }
      ],
      value: [ "MO1", "MO3" ]
    },
    {
      id: 25,
      type: "checkbox",
      required: true,
      value: true
    },
    {
      id: 34,
      type: "checkbox",
      required: true,
      value: false
    }
  ]
}
```

{% hint style="info" %}
Represents the state of the user at the moment of calling the [getter](/javascript-api/overview/functions/memberspace.getmembermetadata) function or receiving it in an [event](/javascript-api/overview/events). As the data changes [MemberSpace.member.info](/javascript-api/overview/events/memberspace.member.info) events are dispatched to publish the updated information.
{% endhint %}

## Attributes

| Name               | Data Type            | Description                                    |
| ------------------ | -------------------- | ---------------------------------------------- |
| id                 | integer              |                                                |
| name               | string               | full name of member                            |
| firstName          | string               |                                                |
| lastName           | string               |                                                |
| email              | string               |                                                |
| profileImageUrl    | string               | url of where they profile image is             |
| memberships        | Membership\[]        | a list of all of the plans a member belongs to |
| customSignupFields | CustomSignupField\[] | a list of all of the members custom fields     |


# Guides

Tips and code examples

{% content-ref url="/pages/-Ma0-6kTt5hRLS83qqc-" %}
[Getting Member Information - SSR](/javascript-api/guides/member-info-ssr)
{% endcontent-ref %}

{% content-ref url="/pages/-MYpGp6XDOZFV4-wVh3-" %}
[Getting Member Information - SPA](/javascript-api/guides/member-info-spa)
{% endcontent-ref %}


# Getting Member Information - SSR

In Server Side Rendering scenarios where you have access to the whole DOM, you can subscribe to our custom [events](/javascript-api/overview/events) before the widget even starts to load. In this case there is no risk of missing the initial event(s).

```javascript
const handleReady = ({ detail }) => {
  // Your code
};        

document.addEventListener('MemberSpace.ready', handleReady);


const handleMemberInfo = ({ detail }) => {
  // Your code
};

document.addEventListener('MemberSpace.member.info', handleMemberInfo);


//MS install code ... Please see the relevant section
var MemberSpace = window.MemberSpace || {subdomain: "YOUR_SUBDOMAIN"};
(function(d){
  var s = d.createElement("script");
  s.src = "https://cdn.memberspace.com/scripts/widgets.js";
  var e = d.getElementsByTagName("script")[0];
  e.parentNode.insertBefore(s,e);
}(document));
```

{% hint style="info" %}
Please check out the [Install Code](/getting-started/install-code) section also.
{% endhint %}


# Getting Member Information - SPA

Specific examples of how to use the Javascript API.

In fronted environments such as single page applications, like React, Angular or Vue, the use of Events will not work as needed because MemberSpace will only fire the [*.ready*](/javascript-api/overview/events#memberspace-ready) event when initialized. For reliable access to [*memberInfo*](/javascript-api/overview/structures-and-examples/structures#memberinfo) use the following Promise function that incorporates a few different components of the MemberSapce JavaScript API.

```javascript
const getMsReadyPromise = () =>
  new Promise(resolve => {
    if (MemberSpace.ready) {
      // Ready event is already fired, so let's not wait for it, it will not be fired again
      resolve(window.MemberSpace.getMemberInfo());
    } else {
      // MS widget is not yet ready, let's subscribe for the event
      const handleReady = ({ detail }) => {
        resolve(detail);
        // Unsubscribe ourselves, this allows GC to collect all related memory
        document.removeEventListener('MemberSpace.ready', handleReady);
      };

      // Listen to ready event
      document.addEventListener('MemberSpace.ready', handleReady);
    }
  });

// ... Somewhere in an async fn
const { memberInfo } = await getMsReadyPromise();

// ... or using Promise.then
getMsReadyPromise().then(({ memberInfo }) => {
  // your code here
});
```

{% hint style="info" %}
This is very useful when you need this information only in specific screens or specific components in frameworks such as React, Angular and Vue.
{% endhint %}


# Migration Guide

Migrating from V1 to V2

We have completely rewritten our frontend system and with that comes the better structure of Events and Functions. We will be sunsetting our legacy JS Hook. If you are using that old system this migration guide is here to help.

## Previous solution

Previously, the MemberSpace V1 widget supported a single onReady style callback hook detailed in the following site: <https://help.memberspace.com/article/162-javascript-hook-developers-only>

This hook has some limitations especially:

* It is only fired once, if user details change host site is not notified
* It contains only limited information
* The host site needs to store the information provided, no way to access the data directly from the widget after the event is triggered
* Can cause memory management issues in the host site depending on the host environment

### Widget v2 uses custom JavaScript events, getters and flags instead:

#### [Events](/javascript-api/overview/events)

* [MemberSpace.ready](/javascript-api/overview/events/memberspace.ready), this behaves much like the previous solution, only fired once. Note: The v2 widget will not refresh the page after a user logs in. If you need to be kept informed about the logged in user, use the `MemberSpace.member.info` event instead.
* [MemberSpace.member.info](/javascript-api/overview/events/memberspace.member.info), This is event is designed to be declarative in nature; It will keep the host site updated about the member's information. Only fired if there is a logged in member.
* [MemberSpace.member.logout](/javascript-api/overview/events/memberspace.member.logout), is fired if a user logs out or is being logged out.&#x20;

#### Getter [functions](/javascript-api/overview/functions)

* [MemberSpace.getMemberInfo](/javascript-api/overview/functions/memberspace.getmemberinfo) Returns MemberInfo (if member is logged in)

#### [Flags](/javascript-api/overview/flags)

* [MemberSpace.ready](/javascript-api/overview/events/memberspace.ready) set to boolean `true` after the widget has been loaded

#### Usage Options

* You can subscribe to the [ready](/javascript-api/overview/events/memberspace.ready) or [member.info](/javascript-api/overview/events/memberspace.member.info) event before initializing the widget, i.e.,

```javascript
  const handleMemberInfo = ({ detail }) => {
    const { memberInfo } = detail;

    // Your code here
  }
  document.addEventListener('MemberSpace.member.info', handleMemberInfo);

  // Standard widget init code
  var MemberSpace = window.MemberSpace || {subdomain: "yourSubdomain"};
  (function(d){
    var s = d.createElement("script");
    s.src = "https://cdn.memberspace.com/scripts/widgets.js";
    var e = d.getElementsByTagName("script")[0];
    e.parentNode.insertBefore(s,e);
  }(document));
```

* If your code runs after the widget initialization, or in an unknown state (for i.e., in a web application, need the info in a specific screen only) You can subscribe to the same events and if the [MemberSpace.ready](/javascript-api/overview/events/memberspace.ready) flag is already true (you probably missed the .[ready](/javascript-api/overview/events/memberspace.ready) and initial .[member.info](/javascript-api/overview/events/memberspace.member.info) events) you can also get the current user state from the [MemberSpace.getMemberInfo](/javascript-api/overview/functions/memberspace.getmemberinfo) getter fn.

### Additional documentation

More information about the events, getters, and the [memberInfo](/javascript-api/overview/structures-and-examples/structures) structure with examples can be found [Here](/javascript-api/overview).


# Showing/Hiding Elements

Protecting a DOM subtree served by the host site

{% hint style="info" %}
It is possible to show and hide parts of the website based on different criteria against the member. You can achieve this by adding custom data attributes to the DOM nodes (HTML tags) you want to conditionally hide / show.

You can only use this feature if you are able to add these attributes. Adding data attributes is not supported by all CMS systems.
{% endhint %}

The attributes **`data-ms-show`** and **`data-ms-hide`** control whether to show or hide the element if the criteria (the value of the data attribute) is met. Let's start with an example

```markup
<div>
  <span data-ms-show="paid">
    Awesome content!
  </span>
  <span data-ms-hide="paid">
    Sorry, you need to be on a paid plan to access this content!
  </span>
</div>
```

{% hint style="info" %}
If you want to avoid any Javascript loading delays for our data attributes (which can look like a flicker) when an element is hidden a split second after page load, we recommend you add inline styling to not display the element. For example:

```html
 <span data-ms-show="paid" style="display:none;">
   Awesome content for paid customers
 </span>
```

{% endhint %}

Keep in mind, the attributes will also do their inverse automatically. For example, the 'Awesome content!' text in the code snippet below would be hidden automatically if the member is not currently logged in and actively on a paid plan:

```html
<span data-ms-show="paid">
  Awesome content!
</span>
```

## Criteria

| <p>Criteria<br>(the value of the data attribute)</p> | Matches if                                                                                                                                                               |
| ---------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| auth                                                 | Member is logged in                                                                                                                                                      |
| free                                                 | Logged in member has at least 1 free plan                                                                                                                                |
| paid                                                 | Logged in member has at least 1 paid plan                                                                                                                                |
| plan(*n*)                                            | <p>Logged in member is part of plan "<em>n</em>"</p><p></p><p>Replace "<em>n</em>" with a plan id.</p><p>Plan ids can be found at the end of the plan's signup link.</p> |
| plan(*n*,*m,*...)                                    | <p>You can also provide multiple plan ids.<br>Matches if the Member is part of any of the listed plans</p>                                                               |

{% hint style="info" %}
If you use **`data-ms-show`** and **`data-ms-hide`**&#x61;ttributes in pairs, using the same criteria (see the example above) you can make it so that either the content or a placeholder (containing a signup link?) is shown.
{% endhint %}


# Member Information

The MemberSpace widget is capable of embedding information about the member into the host site

{% hint style="info" %}
Embedding and filling out member information on the host site can be done by using specific data attributes on DOM nodes (HTML tags). You can only use this feature if you are able to add these attributes. Adding data attributes is not supported by all CMS systems.
{% endhint %}

### Resources

{% content-ref url="/pages/-M\_BNQ\_ggxURf76bof8C" %}
[Embedding Member Info](/embedding/member-information/embedding-member-info)
{% endcontent-ref %}

{% content-ref url="/pages/-M\_BQTgWMAwA0DaQsSQL" %}
[Prefilling Forms](/embedding/member-information/prefilling-forms)
{% endcontent-ref %}

{% content-ref url="/pages/-M\_BuZeJRAZQkKi34wNh" %}
[Available Data Types](/embedding/member-information/available-data-types)
{% endcontent-ref %}


# Embedding Member Info

Non-input dom nodes (HTML tags) can be marked with the data-ms-member-info attribute. If there is a logged in user, the widget will render the relevant information (as a text node) into the element.

### Usage example

```markup
<div>
  Profile Image:
  <img data-ms-member-info="profileImageUrl" />
</div>

<div>
  First Name:
  <span data-ms-member-info="firstName"></span>
</div>
```

{% hint style="info" %}
The rendered information updates as the user information changes

The available data types can be found [here](/embedding/member-information/available-data-types)
{% endhint %}


# Prefilling Forms

Specific input elements (DOM nodes) can be marked to be prefilled by the widget

{% hint style="warning" %}
To not overwrite potential changes the member makes to the prefilled values, filling of these inputs will only be done once, immediately after they are rendered, or after a member logs in. If a member logs out, and another member logs in, the **values will not be updated!**
{% endhint %}

{% hint style="info" %}
The available data types can be found [here](/embedding/member-information/available-data-types)
{% endhint %}

### Examples

#### Text inputs (input type="text", type="email", type="hidden", textarea)

```markup
<div>
  First Name:
  <input
    type="text"
    data-ms-member-info="firstName"
  />
  Last Name:
  <textarea
    data-ms-member-info="lastName"
  >
  </textarea>
</div>
```

{% hint style="info" %}
Text and number type fields will be rendered normally\
Select (radio) type field value will be used to fill out the input\
Multiple select field selected values will be comma and space separated ("apple, pear")\
Checkbox type fields will be rendered as "Yes" or "No", translated per the selected language
{% endhint %}

#### Radio buttons (input type="radio")

```markup
<div>
  Text signup field:
  <div>
    Blue:
    <input
      type="radio"
      value="blue"
      data-ms-member-info="customSignupField22"
    />
  </div>
  <div>
    Red:
    <input
      type="radio"
      value="red"
      data-ms-member-info="customSignupField22"
    />
  </div>
  <div>
    Green:
    <input
      type="radio"
      value="green"
      data-ms-member-info="customSignupField22"
    />
  </div>
</div>
```

{% hint style="info" %}
The value attribute needs to match exactly (case sensitive) to the stored value in order to be selected
{% endhint %}

#### Dropdown (select)

```markup
<div>
  Dropdown:
  <select data-ms-member-info="customSignupField22">
    <option value="blue">Blue</option>
    <option value="red">Red</option>
    <option value="green">Green</option>
  </select>
</div>
```

{% hint style="info" %}
The value attribute needs to match exactly (case sensitive) to the stored value in order to be selected
{% endhint %}

#### Checkbox (input type="checkbox")

```markup
<div>
  Checkbox:
  <input type="checkbox" data-ms-member-info="customSignupField34" />
</div>
```

{% hint style="info" %}
The checkbox will only be updated if the referenced custom input is also of checkbox type
{% endhint %}


# Available Data Types

The following data types are available through the member informatin embed and prefill features

| Attribute                 | Type   | Description                                                                                                    |
| ------------------------- | ------ | -------------------------------------------------------------------------------------------------------------- |
| id                        | number | Internal identifier of the member                                                                              |
| firstName                 | string | First name                                                                                                     |
| lastName                  | string | Last name                                                                                                      |
| name                      | string | First name + white space + Last name                                                                           |
| email                     | string | Email                                                                                                          |
| profileImageUrl           | string | Url for the profile image ( if the member uploaded one )                                                       |
| customSignupFiel&#x64;*#* | any    | <p>The value of a specific custom signup field<br>(<em>#</em> should be replaced with the id of the field)</p> |

{% hint style="info" %}
The id of a custom signup field can be found in the MemberSpace Admin, in the url bar of the browser when editing a custom field:\
\
<https://admin.memberspace.com/sites/0/custom\\_fields/**24**/edit>
{% endhint %}


# Overview

The MemberSpace **Admin API** gives you programmatic, read-only access to your site's data for analytics, reporting, and custom workflows.

{% hint style="info" %}
*Access to the API is limited to customers on higher tier paid plans and currently in beta. If you would like to request access please email **<support@memberspace.com>**.*&#x20;
{% endhint %}

### Base URL

```http
https://api.memberspace.com/admin/v1
```

All endpoints are relative to this base. The version (`v1`) is part of the path; breaking changes ship under a new version.

### Authentication

The API uses bearer-token authentication. Generate an API key in your MemberSpace admin backend, where you can scope it to the data it's allowed to read.

Keys are opaque and prefixed with `ms_admin_`. Send the key on every request:

```http
Authorization: Bearer ms_admin_...
```

A request with a missing, unknown, revoked, or expired key returns `401` with an `accessDenied` error. Treat keys like passwords — store them server-side and never expose them in client-side code.

### Scopes

Each key is limited to the scopes you grant it when you create it. A request that needs a scope the key doesn't have returns `404` (see Errors). Current read scopes:

| Scope               | Grants                               |
| ------------------- | ------------------------------------ |
| `read.plans`        | Read access to plans                 |
| `read.memberships`  | Read access to memberships           |
| `read.members`      | Read access to members               |
| `read.memberEvents` | Read access to member events         |
| `read.contentViews` | Read access to content library views |

### Pagination

List endpoints use forward-only, cursor-based pagination via HTTP headers.

**Request Headers**

| Header          | Description                                                                                                                                          |
| --------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------- |
| `X-Cursor`      | Where to start. Omit for the first page; otherwise pass the previous response's `X-Next-Cursor`. Cursors are opaque — don't parse or construct them. |
| `X-Items-After` | Page size, 1–100 (default 25).                                                                                                                       |

**Response Headers**

| Header                   | Description                                                                 |
| ------------------------ | --------------------------------------------------------------------------- |
| `X-Has-More-Items-After` | `true` if more results follow this page.                                    |
| `X-Next-Cursor`          | Cursor for the next page. Omitted when `X-Has-More-Items-After` is `false`. |

To page through a collection: make the first request without `X-Cursor`, then keep passing the returned `X-Next-Cursor` until `X-Has-More-Items-After` is `false`.

### Responses

Resources are returned as flat JSON with `camelCase` keys. List endpoints return a JSON **array** of objects (there is no envelope or `meta` wrapper); null fields are omitted rather than returned as `null`.

### Errors

The API uses standard HTTP status codes — `2xx` for success, `4xx` for client errors. Error responses always have an `errors` key containing an **array** of error objects:

```json
{
  "errors": [
    {
      "path": "base",
      "code": "accessDenied",
      "message": "Access denied."
    }
  ]
}
```

| Field     | Description                                                                                                                    |
| --------- | ------------------------------------------------------------------------------------------------------------------------------ |
| `path`    | `base` for errors not tied to a specific field; otherwise the dotted path of the offending field (e.g. `community.isEnabled`). |
| `code`    | Machine-readable error code — branch on this, not on `message`.                                                                |
| `message` | Human-readable description.                                                                                                    |

Common statuses and codes:

| Status | Code            | Meaning                                                             |
| ------ | --------------- | ------------------------------------------------------------------- |
| `400`  | `invalidCursor` | The `X-Cursor` value is malformed                                   |
| `401`  | `accessDenied`  | Missing, invalid, or unknown API key                                |
| `404`  | `notFound`      | The resource doesn't exist, or the key lacks the scope to access it |
| `429`  | `rateLimited`   | Too many requests — see the `Retry-After` header                    |

### Rate Limits

When you exceed the rate limit, the API returns `429` with a `rateLimited` error and a `Retry-After` header indicating how many seconds to wait before retrying. Back off for at least that long.

### Support

Questions or feedback during beta? Email **<support@memberspace.com>**.


# Endpoints


# Sites

The sites your API key can access.

## List sites

> Returns the sites the API key has access to (one, for a developer API key).

```json
{"openapi":"3.2.0","info":{"title":"MemberSpace Admin API","version":"1.0.0"},"tags":[{"name":"sites","description":"The sites your API key can access."}],"servers":[{"url":"https://api.memberspace.com/admin/v1","description":"Production"}],"security":[{"apiKey":[]}],"components":{"securitySchemes":{"apiKey":{"type":"http","scheme":"bearer","description":"Opaque Admin API key, prefixed with `ms_admin_`. Pass it as `Authorization: Bearer ms_admin_...`."}},"schemas":{"Site":{"type":"object","description":"A MemberSpace site the API key is authorized to access.","properties":{"id":{"type":"integer","description":"Unique site identifier."},"name":{"type":"string","description":"The site's display name."},"subdomain":{"type":"string","description":"Secondary unique ID of the site"},"domain":{"type":"string","description":"The site's custom domain."},"defaultCurrency":{"type":"string","description":"ISO 4217 currency code used for the site's plans, e.g. `usd`."},"locale":{"type":"string","description":"The 2 char [ISO 639](https://en.wikipedia.org/wiki/List_of_ISO_639_language_codes) language code"},"metrics":{"type":"object","description":"Member counts for the site.","properties":{"activeMemberCount":{"type":"integer","description":"Number of active members on the site."},"freeMemberCount":{"type":"integer","description":"Number of members on free plans."},"paidMemberCount":{"type":"integer","description":"Number of members on paid plans."}},"required":["activeMemberCount","freeMemberCount","paidMemberCount"]},"createdAt":{"type":"string","format":"date-time"}},"required":["id","name","subdomain","domain","defaultCurrency","locale","metrics","createdAt"]},"ApiError":{"type":"object","description":"Error response.","properties":{"errors":{"type":"array","description":"The errors that occurred.","items":{"type":"object","properties":{"path":{"type":"string","description":"`base` for errors not tied to a specific field; otherwise the dotted path of the offending field (e.g. `community.isEnabled`)."},"code":{"type":"string","description":"Machine-readable error code."},"message":{"type":"string","description":"Human-readable error message."}},"required":["path","code","message"]}}},"required":["errors"]}},"headers":{"totalCount":{"description":"Total number of items matching the query across all pages, ignoring pagination.","schema":{"type":"integer"}}},"responses":{"401-AccessDenied":{"description":"The API key is missing, unknown, revoked, or expired.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"403-NotPermitted":{"description":"The request is not permitted for this API key. For example, the site's plan no longer includes Admin API access because the owner canceled or downgraded — the key itself is still valid.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"429-RateLimited":{"description":"Rate limit exceeded. Wait the number of seconds in the Retry-After header before retrying.","headers":{"Retry-After":{"description":"Number of seconds to wait before making another request.","schema":{"type":"integer"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}}}},"paths":{"/sites":{"get":{"tags":["sites"],"summary":"List sites","description":"Returns the sites the API key has access to (one, for a developer API key).","responses":{"200":{"description":"Successful operation","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/Site"}}}},"headers":{"X-Total-Count":{"$ref":"#/components/headers/totalCount"}}},"401":{"$ref":"#/components/responses/401-AccessDenied"},"403":{"$ref":"#/components/responses/403-NotPermitted"},"429":{"$ref":"#/components/responses/429-RateLimited"}}}}}}
```


# Plans

Your site's plans.

## Get a plan

> Returns a single plan by its ID.

```json
{"openapi":"3.2.0","info":{"title":"MemberSpace Admin API","version":"1.0.0"},"tags":[{"name":"plans","description":"Your site's plans."}],"servers":[{"url":"https://api.memberspace.com/admin/v1","description":"Production"}],"security":[{"apiKey":[]}],"components":{"securitySchemes":{"apiKey":{"type":"http","scheme":"bearer","description":"Opaque Admin API key, prefixed with `ms_admin_`. Pass it as `Authorization: Bearer ms_admin_...`."}},"schemas":{"Plan":{"type":"object","description":"A membership plan.","allOf":[{"type":"object","properties":{"id":{"type":"integer","description":"Unique plan identifier."},"publicId":{"type":"string","description":"Unique public plan identifier."},"name":{"type":"string","description":"Plan name."},"description":{"type":"string","description":"Rich text shown during signup."},"status":{"type":"string","enum":["enabled","disabled","archived","deleted"],"description":"Plan lifecycle state. Archived plans are hidden from the dashboard but retain their memberships; deleted plans are read-only."},"isPublic":{"type":"boolean","description":"Whether the plan is publicly listed."},"activeMemberCount":{"type":"integer","description":"Number of active members on the plan."},"colorCode":{"type":"string","description":"Hex color code, auto-assigned when the plan is created."},"contentUrl":{"type":"string","description":"Redirect target for content after login."},"welcomeUrl":{"type":"string","description":"Redirect target for the welcome screen after signup."},"createdAt":{"type":"string","format":"date-time"}},"required":["id","publicId","name","status","isPublic","activeMemberCount","colorCode","createdAt"]},{"oneOf":[{"title":"Free","type":"object","properties":{"type":{"type":"string","enum":["free"]},"expiration":{"$ref":"#/components/schemas/PlanExpiration"}},"required":["type","expiration"]},{"title":"One time payment","type":"object","properties":{"type":{"type":"string","enum":["oneTimePayment"]},"billing":{"type":"object","properties":{"price":{"type":"number","description":"Price in the plan currency."},"currency":{"type":"string","description":"3 letter [ISO currency code](https://en.wikipedia.org/wiki/ISO_4217)."},"taxPercentage":{"type":"number"},"taxDescription":{"type":"string","description":"Rich text shown during signup. Present only with taxPercentage."}},"required":["price","currency"]},"expiration":{"$ref":"#/components/schemas/PlanExpiration"}},"required":["type","billing","expiration"]},{"title":"Multi-payment","type":"object","properties":{"type":{"type":"string","enum":["multiPayment"]},"billing":{"type":"object","properties":{"price":{"type":"number"},"currency":{"type":"string","description":"3 letter [ISO currency code](https://en.wikipedia.org/wiki/ISO_4217)."},"interval":{"type":"object","description":"How often the member is billed.","properties":{"count":{"type":"integer","description":"The number part of the interval. I.e. `3 month`."},"unit":{"type":"string","enum":["day","week","month","year"]}},"required":["count","unit"]},"paymentCountRequired":{"type":"integer","description":"Total number of payments before the plan is paid off."},"trialPeriodDays":{"type":"integer","description":"Length of the free trial in days. Mutually exclusive with signupFee."},"signupFee":{"type":"number","description":"One-time fee charged at signup. Mutually exclusive with trialPeriodDays."},"taxPercentage":{"type":"number"},"taxDescription":{"type":"string","description":"Rich text shown during signup. Present only with taxPercentage."}},"required":["price","currency","interval","paymentCountRequired"]},"expiration":{"$ref":"#/components/schemas/PlanExpiration"}},"required":["type","billing","expiration"]},{"title":"Subscription","type":"object","properties":{"type":{"type":"string","enum":["subscription"]},"billing":{"type":"object","properties":{"price":{"type":"number"},"currency":{"type":"string","description":"3 letter [ISO currency code](https://en.wikipedia.org/wiki/ISO_4217)."},"interval":{"type":"object","description":"How often the member is billed.","properties":{"count":{"type":"integer","description":"The number part of the interval. I.e. `3 month`."},"unit":{"type":"string","enum":["day","week","month","year"]}},"required":["count","unit"]},"billingDate":{"type":"object","description":"Fixed billing date, when set. `day` for monthly, `day` + `month` for yearly.","properties":{"day":{"type":"integer","description":"1-31."},"month":{"type":"integer","description":"1-12."}},"required":["day"]},"trialPeriodDays":{"type":"integer","description":"Length of the free trial in days. Mutually exclusive with signupFee."},"signupFee":{"type":"number","description":"One-time fee charged at signup. Mutually exclusive with trialPeriodDays."},"taxPercentage":{"type":"number"},"taxDescription":{"type":"string","description":"Rich text shown during signup. Present only with taxPercentage."}},"required":["price","currency","interval"]}},"required":["type","billing"]}]}]},"PlanExpiration":{"description":"When a member's access from this plan expires.","oneOf":[{"title":"Never expires","type":"object","properties":{"mode":{"type":"string","enum":["none"]}},"required":["mode"]},{"title":"After time period","type":"object","properties":{"mode":{"type":"string","enum":["interval"]},"count":{"type":"integer"},"unit":{"type":"string","enum":["day","week","month","year"]}},"required":["mode","count","unit"]},{"title":"At specific datetime","type":"object","properties":{"mode":{"type":"string","enum":["specificTime"]},"expiresAt":{"type":"string","format":"date-time"}},"required":["mode","expiresAt"]}]},"ApiError":{"type":"object","description":"Error response.","properties":{"errors":{"type":"array","description":"The errors that occurred.","items":{"type":"object","properties":{"path":{"type":"string","description":"`base` for errors not tied to a specific field; otherwise the dotted path of the offending field (e.g. `community.isEnabled`)."},"code":{"type":"string","description":"Machine-readable error code."},"message":{"type":"string","description":"Human-readable error message."}},"required":["path","code","message"]}}},"required":["errors"]}},"responses":{"401-AccessDenied":{"description":"The API key is missing, unknown, revoked, or expired.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"404-NotFound":{"description":"The resource doesn't exist, or the API key lacks the scope to access it.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"429-RateLimited":{"description":"Rate limit exceeded. Wait the number of seconds in the Retry-After header before retrying.","headers":{"Retry-After":{"description":"Number of seconds to wait before making another request.","schema":{"type":"integer"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}}}},"paths":{"/plans/{id}":{"get":{"tags":["plans"],"summary":"Get a plan","description":"Returns a single plan by its ID.","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"integer"},"description":"The plan's ID."}],"responses":{"200":{"description":"Successful operation","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Plan"}}}},"401":{"$ref":"#/components/responses/401-AccessDenied"},"404":{"$ref":"#/components/responses/404-NotFound"},"429":{"$ref":"#/components/responses/429-RateLimited"}}}}}}
```

## List plans

> Returns the API key's site active (non-archived) plans, ordered by id and paginated with an opaque cursor. Supports filtering by type and status.

```json
{"openapi":"3.2.0","info":{"title":"MemberSpace Admin API","version":"1.0.0"},"tags":[{"name":"plans","description":"Your site's plans."}],"servers":[{"url":"https://api.memberspace.com/admin/v1","description":"Production"}],"security":[{"apiKey":[]}],"components":{"securitySchemes":{"apiKey":{"type":"http","scheme":"bearer","description":"Opaque Admin API key, prefixed with `ms_admin_`. Pass it as `Authorization: Bearer ms_admin_...`."}},"parameters":{"cursor-header":{"name":"X-Cursor","in":"header","required":false,"schema":{"type":"string"},"description":"First item to return, usually equals the previous page response's `X-Next-Cursor` response header. Omit to fetch the first page. Cursors are opaque tokens — do not parse or construct them."},"itemsAfter-header":{"name":"X-Items-After","in":"header","required":false,"schema":{"type":"integer","minimum":1,"maximum":100,"default":25},"description":"Maximum number of results to return beginning with the cursor, or if not sent the first item in the list (1-100, default 25)."}},"schemas":{"Plan":{"type":"object","description":"A membership plan.","allOf":[{"type":"object","properties":{"id":{"type":"integer","description":"Unique plan identifier."},"publicId":{"type":"string","description":"Unique public plan identifier."},"name":{"type":"string","description":"Plan name."},"description":{"type":"string","description":"Rich text shown during signup."},"status":{"type":"string","enum":["enabled","disabled","archived","deleted"],"description":"Plan lifecycle state. Archived plans are hidden from the dashboard but retain their memberships; deleted plans are read-only."},"isPublic":{"type":"boolean","description":"Whether the plan is publicly listed."},"activeMemberCount":{"type":"integer","description":"Number of active members on the plan."},"colorCode":{"type":"string","description":"Hex color code, auto-assigned when the plan is created."},"contentUrl":{"type":"string","description":"Redirect target for content after login."},"welcomeUrl":{"type":"string","description":"Redirect target for the welcome screen after signup."},"createdAt":{"type":"string","format":"date-time"}},"required":["id","publicId","name","status","isPublic","activeMemberCount","colorCode","createdAt"]},{"oneOf":[{"title":"Free","type":"object","properties":{"type":{"type":"string","enum":["free"]},"expiration":{"$ref":"#/components/schemas/PlanExpiration"}},"required":["type","expiration"]},{"title":"One time payment","type":"object","properties":{"type":{"type":"string","enum":["oneTimePayment"]},"billing":{"type":"object","properties":{"price":{"type":"number","description":"Price in the plan currency."},"currency":{"type":"string","description":"3 letter [ISO currency code](https://en.wikipedia.org/wiki/ISO_4217)."},"taxPercentage":{"type":"number"},"taxDescription":{"type":"string","description":"Rich text shown during signup. Present only with taxPercentage."}},"required":["price","currency"]},"expiration":{"$ref":"#/components/schemas/PlanExpiration"}},"required":["type","billing","expiration"]},{"title":"Multi-payment","type":"object","properties":{"type":{"type":"string","enum":["multiPayment"]},"billing":{"type":"object","properties":{"price":{"type":"number"},"currency":{"type":"string","description":"3 letter [ISO currency code](https://en.wikipedia.org/wiki/ISO_4217)."},"interval":{"type":"object","description":"How often the member is billed.","properties":{"count":{"type":"integer","description":"The number part of the interval. I.e. `3 month`."},"unit":{"type":"string","enum":["day","week","month","year"]}},"required":["count","unit"]},"paymentCountRequired":{"type":"integer","description":"Total number of payments before the plan is paid off."},"trialPeriodDays":{"type":"integer","description":"Length of the free trial in days. Mutually exclusive with signupFee."},"signupFee":{"type":"number","description":"One-time fee charged at signup. Mutually exclusive with trialPeriodDays."},"taxPercentage":{"type":"number"},"taxDescription":{"type":"string","description":"Rich text shown during signup. Present only with taxPercentage."}},"required":["price","currency","interval","paymentCountRequired"]},"expiration":{"$ref":"#/components/schemas/PlanExpiration"}},"required":["type","billing","expiration"]},{"title":"Subscription","type":"object","properties":{"type":{"type":"string","enum":["subscription"]},"billing":{"type":"object","properties":{"price":{"type":"number"},"currency":{"type":"string","description":"3 letter [ISO currency code](https://en.wikipedia.org/wiki/ISO_4217)."},"interval":{"type":"object","description":"How often the member is billed.","properties":{"count":{"type":"integer","description":"The number part of the interval. I.e. `3 month`."},"unit":{"type":"string","enum":["day","week","month","year"]}},"required":["count","unit"]},"billingDate":{"type":"object","description":"Fixed billing date, when set. `day` for monthly, `day` + `month` for yearly.","properties":{"day":{"type":"integer","description":"1-31."},"month":{"type":"integer","description":"1-12."}},"required":["day"]},"trialPeriodDays":{"type":"integer","description":"Length of the free trial in days. Mutually exclusive with signupFee."},"signupFee":{"type":"number","description":"One-time fee charged at signup. Mutually exclusive with trialPeriodDays."},"taxPercentage":{"type":"number"},"taxDescription":{"type":"string","description":"Rich text shown during signup. Present only with taxPercentage."}},"required":["price","currency","interval"]}},"required":["type","billing"]}]}]},"PlanExpiration":{"description":"When a member's access from this plan expires.","oneOf":[{"title":"Never expires","type":"object","properties":{"mode":{"type":"string","enum":["none"]}},"required":["mode"]},{"title":"After time period","type":"object","properties":{"mode":{"type":"string","enum":["interval"]},"count":{"type":"integer"},"unit":{"type":"string","enum":["day","week","month","year"]}},"required":["mode","count","unit"]},{"title":"At specific datetime","type":"object","properties":{"mode":{"type":"string","enum":["specificTime"]},"expiresAt":{"type":"string","format":"date-time"}},"required":["mode","expiresAt"]}]},"ApiError":{"type":"object","description":"Error response.","properties":{"errors":{"type":"array","description":"The errors that occurred.","items":{"type":"object","properties":{"path":{"type":"string","description":"`base` for errors not tied to a specific field; otherwise the dotted path of the offending field (e.g. `community.isEnabled`)."},"code":{"type":"string","description":"Machine-readable error code."},"message":{"type":"string","description":"Human-readable error message."}},"required":["path","code","message"]}}},"required":["errors"]}},"headers":{"hasMoreItemsAfter":{"description":"Whether there are more results after the last item in this page.","schema":{"type":"boolean"}},"nextCursor":{"description":"Opaque cursor for the next page. Omitted when X-Has-More-Items-After is false.","schema":{"type":"string"}},"totalCount":{"description":"Total number of items matching the query across all pages, ignoring pagination.","schema":{"type":"integer"}}},"responses":{"400-InvalidRequest":{"description":"Invalid request (e.g. a malformed cursor).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"401-AccessDenied":{"description":"The API key is missing, unknown, revoked, or expired.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"403-NotPermitted":{"description":"The request is not permitted for this API key. For example, the site's plan no longer includes Admin API access because the owner canceled or downgraded — the key itself is still valid.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"404-NotFound":{"description":"The resource doesn't exist, or the API key lacks the scope to access it.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"429-RateLimited":{"description":"Rate limit exceeded. Wait the number of seconds in the Retry-After header before retrying.","headers":{"Retry-After":{"description":"Number of seconds to wait before making another request.","schema":{"type":"integer"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}}}},"paths":{"/plans":{"get":{"tags":["plans"],"summary":"List plans","description":"Returns the API key's site active (non-archived) plans, ordered by id and paginated with an opaque cursor. Supports filtering by type and status.","parameters":[{"$ref":"#/components/parameters/cursor-header"},{"$ref":"#/components/parameters/itemsAfter-header"},{"name":"type","in":"query","required":false,"schema":{"type":"string","enum":["free","oneTimePayment","multiPayment","subscription"]},"description":"Only return plans of this type."},{"name":"status","in":"query","required":false,"schema":{"type":"string","enum":["enabled","disabled"]},"description":"Only return plans with this status. Archived plans are never returned."}],"responses":{"200":{"description":"Successful operation","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/Plan"}}}},"headers":{"X-Has-More-Items-After":{"$ref":"#/components/headers/hasMoreItemsAfter"},"X-Next-Cursor":{"$ref":"#/components/headers/nextCursor"},"X-Total-Count":{"$ref":"#/components/headers/totalCount"}}},"400":{"$ref":"#/components/responses/400-InvalidRequest"},"401":{"$ref":"#/components/responses/401-AccessDenied"},"403":{"$ref":"#/components/responses/403-NotPermitted"},"404":{"$ref":"#/components/responses/404-NotFound"},"429":{"$ref":"#/components/responses/429-RateLimited"}}}}}}
```


# Memberships

Enrollments linking members to the plans they are on.

## List memberships

> Returns the API key's site memberships, ordered by id and paginated with an opaque cursor. Filter by plan, member, status, and creation, renewal, or expiration date to answer questions like "which active memberships on plan X renew in the next three months."

```json
{"openapi":"3.2.0","info":{"title":"MemberSpace Admin API","version":"1.0.0"},"tags":[{"name":"memberships","description":"Enrollments linking members to the plans they are on."}],"servers":[{"url":"https://api.memberspace.com/admin/v1","description":"Production"}],"security":[{"apiKey":[]}],"components":{"securitySchemes":{"apiKey":{"type":"http","scheme":"bearer","description":"Opaque Admin API key, prefixed with `ms_admin_`. Pass it as `Authorization: Bearer ms_admin_...`."}},"parameters":{"cursor-header":{"name":"X-Cursor","in":"header","required":false,"schema":{"type":"string"},"description":"First item to return, usually equals the previous page response's `X-Next-Cursor` response header. Omit to fetch the first page. Cursors are opaque tokens — do not parse or construct them."},"itemsAfter-header":{"name":"X-Items-After","in":"header","required":false,"schema":{"type":"integer","minimum":1,"maximum":100,"default":25},"description":"Maximum number of results to return beginning with the cursor, or if not sent the first item in the list (1-100, default 25)."}},"schemas":{"Membership":{"type":"object","description":"A member's enrollment on a plan.","allOf":[{"type":"object","properties":{"id":{"type":"integer","description":"Unique membership identifier."},"memberId":{"type":"integer","description":"The member this membership belongs to."},"planId":{"type":"integer","description":"The plan this membership is on."},"planPublicId":{"type":"string","description":"Public identifier of the plan."},"status":{"type":"string","enum":["active","trialing","pastDue","inactive","expired","canceled","incomplete","incompleteExpired","unpaid"],"description":"Membership state. `active`, `trialing`, and `pastDue` grant access; the rest do not."},"createdAt":{"type":"string","format":"date-time"}},"required":["id","memberId","planId","planPublicId","status","createdAt"]},{"oneOf":[{"type":"object","title":"Free","properties":{"type":{"type":"string","enum":["free"],"description":"The type of plan this membership is on."},"expiresOn":{"type":"string","format":"date-time","description":"If the membership is set to expire, this is the expiration date"}},"required":["type"]},{"type":"object","title":"One Time Payment","properties":{"type":{"type":"string","enum":["oneTimePayment"],"description":"The type of plan this membership is on."},"expiresOn":{"type":"string","format":"date-time","description":"If the membership is going to expire, this is the expiration date"},"billing":{"type":"object","properties":{"currency":{"type":"string","description":"3 letter [ISO currency code](https://en.wikipedia.org/wiki/ISO_4217)."}},"required":["currency"]}},"required":["type","billing"]},{"type":"object","title":"Subscription","properties":{"type":{"type":"string","enum":["subscription"],"description":"The type of plan this membership is on."},"cancelsOn":{"type":"string","format":"date-time","description":"When a scheduled cancellation takes effect. Absent unless cancellation is pending."},"billing":{"type":"object","properties":{"billingPeriodEnd":{"type":"string","format":"date-time","description":"Indicates the date-time when the current billing period ends, and the next one begins."},"currency":{"type":"string","description":"3 letter [ISO currency code](https://en.wikipedia.org/wiki/ISO_4217)."},"paymentFailure":{"type":"boolean","description":"Whether the most recent payment attempt failed."}},"required":["billingPeriodEnd","currency","paymentFailure"]}},"required":["type","billing"]},{"type":"object","title":"Multi-payment","properties":{"type":{"type":"string","enum":["multiPayment"],"description":"The type of plan this membership is on."},"expiresOn":{"type":"string","format":"date-time","description":"If the membership is going to expire, this is the expiration date"},"billing":{"type":"object","properties":{"billingPeriodEnd":{"type":"string","format":"date-time","description":"Only present if not yet fully paid. Indicates the date-time when the current billing period ends, and the next one (if there will be one) begins."},"currency":{"type":"string","description":"3 letter [ISO currency code](https://en.wikipedia.org/wiki/ISO_4217)."},"paymentCountMade":{"type":"integer","description":"Number of payments completed so far."},"paymentCountRequired":{"type":"integer","description":"Total number of payments required to fully pay off the plan."},"paymentFailure":{"type":"boolean","description":"Whether the most recent payment attempt failed."}},"required":["currency","paymentCountMade","paymentCountRequired","paymentFailure"]}},"required":["type","billing"]}]}]},"ApiError":{"type":"object","description":"Error response.","properties":{"errors":{"type":"array","description":"The errors that occurred.","items":{"type":"object","properties":{"path":{"type":"string","description":"`base` for errors not tied to a specific field; otherwise the dotted path of the offending field (e.g. `community.isEnabled`)."},"code":{"type":"string","description":"Machine-readable error code."},"message":{"type":"string","description":"Human-readable error message."}},"required":["path","code","message"]}}},"required":["errors"]}},"headers":{"hasMoreItemsAfter":{"description":"Whether there are more results after the last item in this page.","schema":{"type":"boolean"}},"nextCursor":{"description":"Opaque cursor for the next page. Omitted when X-Has-More-Items-After is false.","schema":{"type":"string"}},"totalCount":{"description":"Total number of items matching the query across all pages, ignoring pagination.","schema":{"type":"integer"}}},"responses":{"400-InvalidRequest":{"description":"Invalid request (e.g. a malformed cursor).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"401-AccessDenied":{"description":"The API key is missing, unknown, revoked, or expired.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"404-NotFound":{"description":"The resource doesn't exist, or the API key lacks the scope to access it.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"429-RateLimited":{"description":"Rate limit exceeded. Wait the number of seconds in the Retry-After header before retrying.","headers":{"Retry-After":{"description":"Number of seconds to wait before making another request.","schema":{"type":"integer"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}}}},"paths":{"/memberships":{"get":{"tags":["memberships"],"summary":"List memberships","description":"Returns the API key's site memberships, ordered by id and paginated with an opaque cursor. Filter by plan, member, status, and creation, renewal, or expiration date to answer questions like \"which active memberships on plan X renew in the next three months.\"","parameters":[{"$ref":"#/components/parameters/cursor-header"},{"$ref":"#/components/parameters/itemsAfter-header"},{"name":"planId","in":"query","required":false,"schema":{"type":"integer"},"description":"Only return memberships on this plan."},{"name":"memberId","in":"query","required":false,"schema":{"type":"integer"},"description":"Only return memberships belonging to this member."},{"name":"status","in":"query","required":false,"schema":{"type":"string","enum":["active","trialing","pastDue","inactive","expired","canceled","incomplete","incompleteExpired","unpaid"]},"description":"Only return memberships with this status."},{"name":"createdAfter","in":"query","required":false,"schema":{"type":"string","format":"date-time"},"description":"Only return memberships created on or after this ISO 8601 date-time (inclusive)."},{"name":"createdBefore","in":"query","required":false,"schema":{"type":"string","format":"date-time"},"description":"Only return memberships created before this ISO 8601 date-time (exclusive)."},{"name":"renewsAfter","in":"query","required":false,"schema":{"type":"string","format":"date-time"},"description":"Only return memberships whose billing.billingPeriodEnd is on or after this ISO 8601 date-time (inclusive)."},{"name":"renewsBefore","in":"query","required":false,"schema":{"type":"string","format":"date-time"},"description":"Only return memberships whose billing.billingPeriodEnd is before this ISO 8601 date-time (exclusive)."},{"name":"expiresAfter","in":"query","required":false,"schema":{"type":"string","format":"date-time"},"description":"Only return memberships whose expiresAt is on or after this ISO 8601 date-time (inclusive)."},{"name":"expiresBefore","in":"query","required":false,"schema":{"type":"string","format":"date-time"},"description":"Only return memberships whose expiresAt is before this ISO 8601 date-time (exclusive)."}],"responses":{"200":{"description":"Successful operation","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/Membership"}}}},"headers":{"X-Has-More-Items-After":{"$ref":"#/components/headers/hasMoreItemsAfter"},"X-Next-Cursor":{"$ref":"#/components/headers/nextCursor"},"X-Total-Count":{"$ref":"#/components/headers/totalCount"}}},"400":{"$ref":"#/components/responses/400-InvalidRequest"},"401":{"$ref":"#/components/responses/401-AccessDenied"},"404":{"$ref":"#/components/responses/404-NotFound"},"429":{"$ref":"#/components/responses/429-RateLimited"}}}}}}
```


# Members

The people who have signed up on your site.

## List members

> Returns the API key's site members, ordered by id and paginated with an opaque cursor. Supports filtering by first name, last name, email, status, and signup date.

```json
{"openapi":"3.2.0","info":{"title":"MemberSpace Admin API","version":"1.0.0"},"tags":[{"name":"members","description":"The people who have signed up on your site."}],"servers":[{"url":"https://api.memberspace.com/admin/v1","description":"Production"}],"security":[{"apiKey":[]}],"components":{"securitySchemes":{"apiKey":{"type":"http","scheme":"bearer","description":"Opaque Admin API key, prefixed with `ms_admin_`. Pass it as `Authorization: Bearer ms_admin_...`."}},"parameters":{"cursor-header":{"name":"X-Cursor","in":"header","required":false,"schema":{"type":"string"},"description":"First item to return, usually equals the previous page response's `X-Next-Cursor` response header. Omit to fetch the first page. Cursors are opaque tokens — do not parse or construct them."},"itemsAfter-header":{"name":"X-Items-After","in":"header","required":false,"schema":{"type":"integer","minimum":1,"maximum":100,"default":25},"description":"Maximum number of results to return beginning with the cursor, or if not sent the first item in the list (1-100, default 25)."}},"schemas":{"Member":{"type":"object","description":"A person who has signed up on your site.","properties":{"id":{"type":"integer","description":"Unique member identifier."},"email":{"type":"string","format":"email","description":"Member's email address."},"firstName":{"type":"string","description":"Member's first name."},"lastName":{"type":"string","description":"Member's last name."},"status":{"type":"string","enum":["approved","approvedManually","awaitingApproval","denied","imported","invited","removed"],"description":"Member account state. `approved` and `approvedManually` can access member-only content; the others cannot."},"createdAt":{"type":"string","format":"date-time"}},"required":["id","email","firstName","lastName","status","createdAt"]},"ApiError":{"type":"object","description":"Error response.","properties":{"errors":{"type":"array","description":"The errors that occurred.","items":{"type":"object","properties":{"path":{"type":"string","description":"`base` for errors not tied to a specific field; otherwise the dotted path of the offending field (e.g. `community.isEnabled`)."},"code":{"type":"string","description":"Machine-readable error code."},"message":{"type":"string","description":"Human-readable error message."}},"required":["path","code","message"]}}},"required":["errors"]}},"headers":{"hasMoreItemsAfter":{"description":"Whether there are more results after the last item in this page.","schema":{"type":"boolean"}},"nextCursor":{"description":"Opaque cursor for the next page. Omitted when X-Has-More-Items-After is false.","schema":{"type":"string"}},"totalCount":{"description":"Total number of items matching the query across all pages, ignoring pagination.","schema":{"type":"integer"}}},"responses":{"400-InvalidRequest":{"description":"Invalid request (e.g. a malformed cursor).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"401-AccessDenied":{"description":"The API key is missing, unknown, revoked, or expired.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"404-NotFound":{"description":"The resource doesn't exist, or the API key lacks the scope to access it.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"429-RateLimited":{"description":"Rate limit exceeded. Wait the number of seconds in the Retry-After header before retrying.","headers":{"Retry-After":{"description":"Number of seconds to wait before making another request.","schema":{"type":"integer"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}}}},"paths":{"/members":{"get":{"tags":["members"],"summary":"List members","description":"Returns the API key's site members, ordered by id and paginated with an opaque cursor. Supports filtering by first name, last name, email, status, and signup date.","parameters":[{"$ref":"#/components/parameters/cursor-header"},{"$ref":"#/components/parameters/itemsAfter-header"},{"name":"firstName","in":"query","required":false,"schema":{"type":"string"},"description":"Exact match against the member's first name."},{"name":"lastName","in":"query","required":false,"schema":{"type":"string"},"description":"Exact match against the member's last name."},{"name":"email","in":"query","required":false,"schema":{"type":"string"},"description":"Exact match against the member's email."},{"name":"status","in":"query","required":false,"schema":{"type":"string","enum":["approved","approvedManually","awaitingApproval","denied","imported","invited","removed"]},"description":"Only return members with this status."},{"name":"createdAfter","in":"query","required":false,"schema":{"type":"string","format":"date-time"},"description":"Only return members created on or after this ISO 8601 date-time (inclusive)."},{"name":"createdBefore","in":"query","required":false,"schema":{"type":"string","format":"date-time"},"description":"Only return members created before this ISO 8601 date-time (exclusive)."}],"responses":{"200":{"description":"Successful operation","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/Member"}}}},"headers":{"X-Has-More-Items-After":{"$ref":"#/components/headers/hasMoreItemsAfter"},"X-Next-Cursor":{"$ref":"#/components/headers/nextCursor"},"X-Total-Count":{"$ref":"#/components/headers/totalCount"}}},"400":{"$ref":"#/components/responses/400-InvalidRequest"},"401":{"$ref":"#/components/responses/401-AccessDenied"},"404":{"$ref":"#/components/responses/404-NotFound"},"429":{"$ref":"#/components/responses/429-RateLimited"}}}}}}
```


# Member Events

Activity events recorded for your members.

## List member events

> Returns activity events recorded for your members, ordered by id and paginated with an opaque cursor. Filter by member, event type, and date.

```json
{"openapi":"3.2.0","info":{"title":"MemberSpace Admin API","version":"1.0.0"},"tags":[{"name":"memberEvents","description":"Activity events recorded for your members."}],"servers":[{"url":"https://api.memberspace.com/admin/v1","description":"Production"}],"security":[{"apiKey":[]}],"components":{"securitySchemes":{"apiKey":{"type":"http","scheme":"bearer","description":"Opaque Admin API key, prefixed with `ms_admin_`. Pass it as `Authorization: Bearer ms_admin_...`."}},"parameters":{"cursor-header":{"name":"X-Cursor","in":"header","required":false,"schema":{"type":"string"},"description":"First item to return, usually equals the previous page response's `X-Next-Cursor` response header. Omit to fetch the first page. Cursors are opaque tokens — do not parse or construct them."},"itemsAfter-header":{"name":"X-Items-After","in":"header","required":false,"schema":{"type":"integer","minimum":1,"maximum":100,"default":25},"description":"Maximum number of results to return beginning with the cursor, or if not sent the first item in the list (1-100, default 25)."}},"schemas":{"MemberEvent":{"type":"object","description":"An activity event recorded for a member.","properties":{"id":{"type":"integer","description":"Unique event identifier."},"memberId":{"type":"integer","description":"The member the event relates to."},"eventType":{"type":"string","enum":["abandonedConversion","acceptedInvitation","accountCreated","cardUpdated","changedEmail","chargeCreated","dunningConversion","email","logIn","logOut","planAdded","planChanged","planDowngraded","planExpired","planRemoved","planSetToCancel","planUpgraded","resetPassword"],"description":"The specific event that occurred."},"details":{"type":"object","description":"Event-specific metadata. Always present (empty object when the event has none); its fields vary by eventType and include `planId` when the event relates to a plan.","additionalProperties":true},"createdAt":{"type":"string","format":"date-time","description":"When the event occurred."}},"required":["id","memberId","eventType","details","createdAt"]},"ApiError":{"type":"object","description":"Error response.","properties":{"errors":{"type":"array","description":"The errors that occurred.","items":{"type":"object","properties":{"path":{"type":"string","description":"`base` for errors not tied to a specific field; otherwise the dotted path of the offending field (e.g. `community.isEnabled`)."},"code":{"type":"string","description":"Machine-readable error code."},"message":{"type":"string","description":"Human-readable error message."}},"required":["path","code","message"]}}},"required":["errors"]}},"headers":{"hasMoreItemsAfter":{"description":"Whether there are more results after the last item in this page.","schema":{"type":"boolean"}},"nextCursor":{"description":"Opaque cursor for the next page. Omitted when X-Has-More-Items-After is false.","schema":{"type":"string"}},"totalCount":{"description":"Total number of items matching the query across all pages, ignoring pagination.","schema":{"type":"integer"}}},"responses":{"400-InvalidRequest":{"description":"Invalid request (e.g. a malformed cursor).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"401-AccessDenied":{"description":"The API key is missing, unknown, revoked, or expired.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"404-NotFound":{"description":"The resource doesn't exist, or the API key lacks the scope to access it.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"429-RateLimited":{"description":"Rate limit exceeded. Wait the number of seconds in the Retry-After header before retrying.","headers":{"Retry-After":{"description":"Number of seconds to wait before making another request.","schema":{"type":"integer"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}}}},"paths":{"/memberEvents":{"get":{"tags":["memberEvents"],"summary":"List member events","description":"Returns activity events recorded for your members, ordered by id and paginated with an opaque cursor. Filter by member, event type, and date.","parameters":[{"$ref":"#/components/parameters/cursor-header"},{"$ref":"#/components/parameters/itemsAfter-header"},{"name":"memberId","in":"query","required":false,"schema":{"type":"integer"},"description":"Only return events for this member."},{"name":"eventType[]","in":"query","required":false,"explode":true,"schema":{"type":"array","items":{"type":"string","enum":["abandonedConversion","acceptedInvitation","accountCreated","cardUpdated","changedEmail","chargeCreated","dunningConversion","email","logIn","logOut","planAdded","planChanged","planDowngraded","planExpired","planRemoved","planSetToCancel","planUpgraded","resetPassword"]}},"description":"Only return events of these types. Repeat the parameter to filter by multiple types (e.g. `?eventType[]=planUpgraded&eventType[]=planAdded`)."},{"name":"createdAfter","in":"query","required":false,"schema":{"type":"string","format":"date-time"},"description":"Only return events on or after this ISO 8601 date-time (inclusive)."},{"name":"createdBefore","in":"query","required":false,"schema":{"type":"string","format":"date-time"},"description":"Only return events before this ISO 8601 date-time (exclusive)."}],"responses":{"200":{"description":"Successful operation","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/MemberEvent"}}}},"headers":{"X-Has-More-Items-After":{"$ref":"#/components/headers/hasMoreItemsAfter"},"X-Next-Cursor":{"$ref":"#/components/headers/nextCursor"},"X-Total-Count":{"$ref":"#/components/headers/totalCount"}}},"400":{"$ref":"#/components/responses/400-InvalidRequest"},"401":{"$ref":"#/components/responses/401-AccessDenied"},"404":{"$ref":"#/components/responses/404-NotFound"},"429":{"$ref":"#/components/responses/429-RateLimited"}}}}}}
```


# Content Library

Your site's Content Library — content items, spaces, and member activity such as views.

## List content views

> Returns records of members successfully viewing your Content Library content, ordered by id and paginated with an opaque cursor. Filter by member, content item, and view date.

```json
{"openapi":"3.2.0","info":{"title":"MemberSpace Admin API","version":"1.0.0"},"tags":[{"name":"contentLibrary","description":"Your site's Content Library — content items, spaces, and member activity such as views."}],"servers":[{"url":"https://api.memberspace.com/admin/v1","description":"Production"}],"security":[{"apiKey":[]}],"components":{"securitySchemes":{"apiKey":{"type":"http","scheme":"bearer","description":"Opaque Admin API key, prefixed with `ms_admin_`. Pass it as `Authorization: Bearer ms_admin_...`."}},"parameters":{"cursor-header":{"name":"X-Cursor","in":"header","required":false,"schema":{"type":"string"},"description":"First item to return, usually equals the previous page response's `X-Next-Cursor` response header. Omit to fetch the first page. Cursors are opaque tokens — do not parse or construct them."},"itemsAfter-header":{"name":"X-Items-After","in":"header","required":false,"schema":{"type":"integer","minimum":1,"maximum":100,"default":25},"description":"Maximum number of results to return beginning with the cursor, or if not sent the first item in the list (1-100, default 25)."}},"schemas":{"ContentLibraryView":{"type":"object","description":"A record of a member successfully viewing a piece of your Content Library content.","properties":{"id":{"type":"integer","description":"Unique view identifier."},"memberId":{"type":"integer","description":"The member who viewed the content."},"node":{"type":"object","description":"The content item that was viewed.","properties":{"id":{"type":"integer","description":"Unique node identifier."},"name":{"type":"string","description":"Name of the content item."},"type":{"type":"string","enum":["file","link","article","post","protectedPage","video"],"description":"The kind of content item."}},"required":["id","name","type"]},"spaceNode":{"type":"object","description":"The space (collection) the content belongs to.","properties":{"id":{"type":"integer","description":"Unique space node identifier."},"name":{"type":"string","description":"Name of the space."}},"required":["id","name"]},"viewedAt":{"type":"string","format":"date-time","description":"When the content was viewed."}},"required":["id","memberId","node","spaceNode","viewedAt"]},"ApiError":{"type":"object","description":"Error response.","properties":{"errors":{"type":"array","description":"The errors that occurred.","items":{"type":"object","properties":{"path":{"type":"string","description":"`base` for errors not tied to a specific field; otherwise the dotted path of the offending field (e.g. `community.isEnabled`)."},"code":{"type":"string","description":"Machine-readable error code."},"message":{"type":"string","description":"Human-readable error message."}},"required":["path","code","message"]}}},"required":["errors"]}},"headers":{"hasMoreItemsAfter":{"description":"Whether there are more results after the last item in this page.","schema":{"type":"boolean"}},"nextCursor":{"description":"Opaque cursor for the next page. Omitted when X-Has-More-Items-After is false.","schema":{"type":"string"}},"totalCount":{"description":"Total number of items matching the query across all pages, ignoring pagination.","schema":{"type":"integer"}}},"responses":{"400-InvalidRequest":{"description":"Invalid request (e.g. a malformed cursor).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"401-AccessDenied":{"description":"The API key is missing, unknown, revoked, or expired.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"404-NotFound":{"description":"The resource doesn't exist, or the API key lacks the scope to access it.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"429-RateLimited":{"description":"Rate limit exceeded. Wait the number of seconds in the Retry-After header before retrying.","headers":{"Retry-After":{"description":"Number of seconds to wait before making another request.","schema":{"type":"integer"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}}}},"paths":{"/contentLibrary/views":{"get":{"tags":["contentLibrary"],"summary":"List content views","description":"Returns records of members successfully viewing your Content Library content, ordered by id and paginated with an opaque cursor. Filter by member, content item, and view date.","parameters":[{"$ref":"#/components/parameters/cursor-header"},{"$ref":"#/components/parameters/itemsAfter-header"},{"name":"memberId","in":"query","required":false,"schema":{"type":"integer"},"description":"Only return views by this member."},{"name":"nodeId","in":"query","required":false,"schema":{"type":"integer"},"description":"Only return views of this content item."},{"name":"viewedAfter","in":"query","required":false,"schema":{"type":"string","format":"date-time"},"description":"Only return views on or after this ISO 8601 date-time (inclusive)."},{"name":"viewedBefore","in":"query","required":false,"schema":{"type":"string","format":"date-time"},"description":"Only return views before this ISO 8601 date-time (exclusive)."}],"responses":{"200":{"description":"Successful operation","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/ContentLibraryView"}}}},"headers":{"X-Has-More-Items-After":{"$ref":"#/components/headers/hasMoreItemsAfter"},"X-Next-Cursor":{"$ref":"#/components/headers/nextCursor"},"X-Total-Count":{"$ref":"#/components/headers/totalCount"}}},"400":{"$ref":"#/components/responses/400-InvalidRequest"},"401":{"$ref":"#/components/responses/401-AccessDenied"},"404":{"$ref":"#/components/responses/404-NotFound"},"429":{"$ref":"#/components/responses/429-RateLimited"}}}}}}
```


# Models

## The Site object

```json
{"openapi":"3.2.0","info":{"title":"MemberSpace Admin API","version":"1.0.0"},"components":{"schemas":{"Site":{"type":"object","description":"A MemberSpace site the API key is authorized to access.","properties":{"id":{"type":"integer","description":"Unique site identifier."},"name":{"type":"string","description":"The site's display name."},"subdomain":{"type":"string","description":"Secondary unique ID of the site"},"domain":{"type":"string","description":"The site's custom domain."},"defaultCurrency":{"type":"string","description":"ISO 4217 currency code used for the site's plans, e.g. `usd`."},"locale":{"type":"string","description":"The 2 char [ISO 639](https://en.wikipedia.org/wiki/List_of_ISO_639_language_codes) language code"},"metrics":{"type":"object","description":"Member counts for the site.","properties":{"activeMemberCount":{"type":"integer","description":"Number of active members on the site."},"freeMemberCount":{"type":"integer","description":"Number of members on free plans."},"paidMemberCount":{"type":"integer","description":"Number of members on paid plans."}},"required":["activeMemberCount","freeMemberCount","paidMemberCount"]},"createdAt":{"type":"string","format":"date-time"}},"required":["id","name","subdomain","domain","defaultCurrency","locale","metrics","createdAt"]}}}}
```

## The PlanExpiration object

```json
{"openapi":"3.2.0","info":{"title":"MemberSpace Admin API","version":"1.0.0"},"components":{"schemas":{"PlanExpiration":{"description":"When a member's access from this plan expires.","oneOf":[{"title":"Never expires","type":"object","properties":{"mode":{"type":"string","enum":["none"]}},"required":["mode"]},{"title":"After time period","type":"object","properties":{"mode":{"type":"string","enum":["interval"]},"count":{"type":"integer"},"unit":{"type":"string","enum":["day","week","month","year"]}},"required":["mode","count","unit"]},{"title":"At specific datetime","type":"object","properties":{"mode":{"type":"string","enum":["specificTime"]},"expiresAt":{"type":"string","format":"date-time"}},"required":["mode","expiresAt"]}]}}}}
```

## The Plan object

```json
{"openapi":"3.2.0","info":{"title":"MemberSpace Admin API","version":"1.0.0"},"components":{"schemas":{"Plan":{"type":"object","description":"A membership plan.","allOf":[{"type":"object","properties":{"id":{"type":"integer","description":"Unique plan identifier."},"publicId":{"type":"string","description":"Unique public plan identifier."},"name":{"type":"string","description":"Plan name."},"description":{"type":"string","description":"Rich text shown during signup."},"status":{"type":"string","enum":["enabled","disabled","archived","deleted"],"description":"Plan lifecycle state. Archived plans are hidden from the dashboard but retain their memberships; deleted plans are read-only."},"isPublic":{"type":"boolean","description":"Whether the plan is publicly listed."},"activeMemberCount":{"type":"integer","description":"Number of active members on the plan."},"colorCode":{"type":"string","description":"Hex color code, auto-assigned when the plan is created."},"contentUrl":{"type":"string","description":"Redirect target for content after login."},"welcomeUrl":{"type":"string","description":"Redirect target for the welcome screen after signup."},"createdAt":{"type":"string","format":"date-time"}},"required":["id","publicId","name","status","isPublic","activeMemberCount","colorCode","createdAt"]},{"oneOf":[{"title":"Free","type":"object","properties":{"type":{"type":"string","enum":["free"]},"expiration":{"$ref":"#/components/schemas/PlanExpiration"}},"required":["type","expiration"]},{"title":"One time payment","type":"object","properties":{"type":{"type":"string","enum":["oneTimePayment"]},"billing":{"type":"object","properties":{"price":{"type":"number","description":"Price in the plan currency."},"currency":{"type":"string","description":"3 letter [ISO currency code](https://en.wikipedia.org/wiki/ISO_4217)."},"taxPercentage":{"type":"number"},"taxDescription":{"type":"string","description":"Rich text shown during signup. Present only with taxPercentage."}},"required":["price","currency"]},"expiration":{"$ref":"#/components/schemas/PlanExpiration"}},"required":["type","billing","expiration"]},{"title":"Multi-payment","type":"object","properties":{"type":{"type":"string","enum":["multiPayment"]},"billing":{"type":"object","properties":{"price":{"type":"number"},"currency":{"type":"string","description":"3 letter [ISO currency code](https://en.wikipedia.org/wiki/ISO_4217)."},"interval":{"type":"object","description":"How often the member is billed.","properties":{"count":{"type":"integer","description":"The number part of the interval. I.e. `3 month`."},"unit":{"type":"string","enum":["day","week","month","year"]}},"required":["count","unit"]},"paymentCountRequired":{"type":"integer","description":"Total number of payments before the plan is paid off."},"trialPeriodDays":{"type":"integer","description":"Length of the free trial in days. Mutually exclusive with signupFee."},"signupFee":{"type":"number","description":"One-time fee charged at signup. Mutually exclusive with trialPeriodDays."},"taxPercentage":{"type":"number"},"taxDescription":{"type":"string","description":"Rich text shown during signup. Present only with taxPercentage."}},"required":["price","currency","interval","paymentCountRequired"]},"expiration":{"$ref":"#/components/schemas/PlanExpiration"}},"required":["type","billing","expiration"]},{"title":"Subscription","type":"object","properties":{"type":{"type":"string","enum":["subscription"]},"billing":{"type":"object","properties":{"price":{"type":"number"},"currency":{"type":"string","description":"3 letter [ISO currency code](https://en.wikipedia.org/wiki/ISO_4217)."},"interval":{"type":"object","description":"How often the member is billed.","properties":{"count":{"type":"integer","description":"The number part of the interval. I.e. `3 month`."},"unit":{"type":"string","enum":["day","week","month","year"]}},"required":["count","unit"]},"billingDate":{"type":"object","description":"Fixed billing date, when set. `day` for monthly, `day` + `month` for yearly.","properties":{"day":{"type":"integer","description":"1-31."},"month":{"type":"integer","description":"1-12."}},"required":["day"]},"trialPeriodDays":{"type":"integer","description":"Length of the free trial in days. Mutually exclusive with signupFee."},"signupFee":{"type":"number","description":"One-time fee charged at signup. Mutually exclusive with trialPeriodDays."},"taxPercentage":{"type":"number"},"taxDescription":{"type":"string","description":"Rich text shown during signup. Present only with taxPercentage."}},"required":["price","currency","interval"]}},"required":["type","billing"]}]}]},"PlanExpiration":{"description":"When a member's access from this plan expires.","oneOf":[{"title":"Never expires","type":"object","properties":{"mode":{"type":"string","enum":["none"]}},"required":["mode"]},{"title":"After time period","type":"object","properties":{"mode":{"type":"string","enum":["interval"]},"count":{"type":"integer"},"unit":{"type":"string","enum":["day","week","month","year"]}},"required":["mode","count","unit"]},{"title":"At specific datetime","type":"object","properties":{"mode":{"type":"string","enum":["specificTime"]},"expiresAt":{"type":"string","format":"date-time"}},"required":["mode","expiresAt"]}]}}}}
```

## The Membership object

```json
{"openapi":"3.2.0","info":{"title":"MemberSpace Admin API","version":"1.0.0"},"components":{"schemas":{"Membership":{"type":"object","description":"A member's enrollment on a plan.","allOf":[{"type":"object","properties":{"id":{"type":"integer","description":"Unique membership identifier."},"memberId":{"type":"integer","description":"The member this membership belongs to."},"planId":{"type":"integer","description":"The plan this membership is on."},"planPublicId":{"type":"string","description":"Public identifier of the plan."},"status":{"type":"string","enum":["active","trialing","pastDue","inactive","expired","canceled","incomplete","incompleteExpired","unpaid"],"description":"Membership state. `active`, `trialing`, and `pastDue` grant access; the rest do not."},"createdAt":{"type":"string","format":"date-time"}},"required":["id","memberId","planId","planPublicId","status","createdAt"]},{"oneOf":[{"type":"object","title":"Free","properties":{"type":{"type":"string","enum":["free"],"description":"The type of plan this membership is on."},"expiresOn":{"type":"string","format":"date-time","description":"If the membership is set to expire, this is the expiration date"}},"required":["type"]},{"type":"object","title":"One Time Payment","properties":{"type":{"type":"string","enum":["oneTimePayment"],"description":"The type of plan this membership is on."},"expiresOn":{"type":"string","format":"date-time","description":"If the membership is going to expire, this is the expiration date"},"billing":{"type":"object","properties":{"currency":{"type":"string","description":"3 letter [ISO currency code](https://en.wikipedia.org/wiki/ISO_4217)."}},"required":["currency"]}},"required":["type","billing"]},{"type":"object","title":"Subscription","properties":{"type":{"type":"string","enum":["subscription"],"description":"The type of plan this membership is on."},"cancelsOn":{"type":"string","format":"date-time","description":"When a scheduled cancellation takes effect. Absent unless cancellation is pending."},"billing":{"type":"object","properties":{"billingPeriodEnd":{"type":"string","format":"date-time","description":"Indicates the date-time when the current billing period ends, and the next one begins."},"currency":{"type":"string","description":"3 letter [ISO currency code](https://en.wikipedia.org/wiki/ISO_4217)."},"paymentFailure":{"type":"boolean","description":"Whether the most recent payment attempt failed."}},"required":["billingPeriodEnd","currency","paymentFailure"]}},"required":["type","billing"]},{"type":"object","title":"Multi-payment","properties":{"type":{"type":"string","enum":["multiPayment"],"description":"The type of plan this membership is on."},"expiresOn":{"type":"string","format":"date-time","description":"If the membership is going to expire, this is the expiration date"},"billing":{"type":"object","properties":{"billingPeriodEnd":{"type":"string","format":"date-time","description":"Only present if not yet fully paid. Indicates the date-time when the current billing period ends, and the next one (if there will be one) begins."},"currency":{"type":"string","description":"3 letter [ISO currency code](https://en.wikipedia.org/wiki/ISO_4217)."},"paymentCountMade":{"type":"integer","description":"Number of payments completed so far."},"paymentCountRequired":{"type":"integer","description":"Total number of payments required to fully pay off the plan."},"paymentFailure":{"type":"boolean","description":"Whether the most recent payment attempt failed."}},"required":["currency","paymentCountMade","paymentCountRequired","paymentFailure"]}},"required":["type","billing"]}]}]}}}}
```

## The MemberEvent object

```json
{"openapi":"3.2.0","info":{"title":"MemberSpace Admin API","version":"1.0.0"},"components":{"schemas":{"MemberEvent":{"type":"object","description":"An activity event recorded for a member.","properties":{"id":{"type":"integer","description":"Unique event identifier."},"memberId":{"type":"integer","description":"The member the event relates to."},"eventType":{"type":"string","enum":["abandonedConversion","acceptedInvitation","accountCreated","cardUpdated","changedEmail","chargeCreated","dunningConversion","email","logIn","logOut","planAdded","planChanged","planDowngraded","planExpired","planRemoved","planSetToCancel","planUpgraded","resetPassword"],"description":"The specific event that occurred."},"details":{"type":"object","description":"Event-specific metadata. Always present (empty object when the event has none); its fields vary by eventType and include `planId` when the event relates to a plan.","additionalProperties":true},"createdAt":{"type":"string","format":"date-time","description":"When the event occurred."}},"required":["id","memberId","eventType","details","createdAt"]}}}}
```

## The Member object

```json
{"openapi":"3.2.0","info":{"title":"MemberSpace Admin API","version":"1.0.0"},"components":{"schemas":{"Member":{"type":"object","description":"A person who has signed up on your site.","properties":{"id":{"type":"integer","description":"Unique member identifier."},"email":{"type":"string","format":"email","description":"Member's email address."},"firstName":{"type":"string","description":"Member's first name."},"lastName":{"type":"string","description":"Member's last name."},"status":{"type":"string","enum":["approved","approvedManually","awaitingApproval","denied","imported","invited","removed"],"description":"Member account state. `approved` and `approvedManually` can access member-only content; the others cannot."},"createdAt":{"type":"string","format":"date-time"}},"required":["id","email","firstName","lastName","status","createdAt"]}}}}
```

## The ContentLibraryView object

```json
{"openapi":"3.2.0","info":{"title":"MemberSpace Admin API","version":"1.0.0"},"components":{"schemas":{"ContentLibraryView":{"type":"object","description":"A record of a member successfully viewing a piece of your Content Library content.","properties":{"id":{"type":"integer","description":"Unique view identifier."},"memberId":{"type":"integer","description":"The member who viewed the content."},"node":{"type":"object","description":"The content item that was viewed.","properties":{"id":{"type":"integer","description":"Unique node identifier."},"name":{"type":"string","description":"Name of the content item."},"type":{"type":"string","enum":["file","link","article","post","protectedPage","video"],"description":"The kind of content item."}},"required":["id","name","type"]},"spaceNode":{"type":"object","description":"The space (collection) the content belongs to.","properties":{"id":{"type":"integer","description":"Unique space node identifier."},"name":{"type":"string","description":"Name of the space."}},"required":["id","name"]},"viewedAt":{"type":"string","format":"date-time","description":"When the content was viewed."}},"required":["id","memberId","node","spaceNode","viewedAt"]}}}}
```

## The ApiError object

```json
{"openapi":"3.2.0","info":{"title":"MemberSpace Admin API","version":"1.0.0"},"components":{"schemas":{"ApiError":{"type":"object","description":"Error response.","properties":{"errors":{"type":"array","description":"The errors that occurred.","items":{"type":"object","properties":{"path":{"type":"string","description":"`base` for errors not tied to a specific field; otherwise the dotted path of the offending field (e.g. `community.isEnabled`)."},"code":{"type":"string","description":"Machine-readable error code."},"message":{"type":"string","description":"Human-readable error message."}},"required":["path","code","message"]}}},"required":["errors"]}}}}
```


