Skip to content

Ghost Membership API — Custom Signup Flows and Portal Integration

DodaTech Updated 2026-06-28 10 min read

In this tutorial, you'll learn how to extend Ghost memberships using the Membership API — building custom signup flows, integrating the membership portal into custom frontends, managing members programmatically, and creating custom member experiences.

What You'll Learn

  • The Ghost Membership API overview
  • Custom signup flows with the Portal API
  • Portal integration into custom frontends (React, Vue, static sites)
  • Managing members with the Admin API
  • Creating custom member registration pages
  • Building a custom account dashboard
  • Webhook integration for member events
  • Member authentication with magic links
  • Extending Ghost memberships with external services

Why It Matters

The default Ghost Portal is clean and functional, but sometimes you need more — a custom signup page that matches your brand precisely, a multi-step registration flow, or integration with an existing user database. The Membership API and Portal integration points let you build custom member experiences while leveraging Ghost's built-in authentication, Stripe integration, and member management.

Real-World Use

A SaaS company uses Ghost for their documentation and blog. They want users who sign up for their SaaS product to automatically get membership access to the documentation site. They use Ghost's Admin API to create members when users sign up for the SaaS, and they integrate the Ghost Portal into their existing React app for a seamless login experience.

Learning Path

flowchart LR
  A["Subscriber Management"] --> B["Membership API
You are here"]:::current B --> C["Content API"] C --> D["Admin API"] classDef current fill:#38bdf8,color:#0f172a,stroke-width:2px

Ghost Membership API Overview

Ghost provides several interfaces for membership operations:

  1. Portal JavaScript SDK — Drop-in modal for signup, login, account management
  2. Admin API — Server-side member CRUD operations
  3. Webhooks — Real-time notifications for member events
  4. Magic Link API — Programmatic authentication

The Portal JavaScript SDK

The Portal is a JavaScript module that provides the member-facing UI. It is included by default in Ghost themes but can be customized or integrated into external sites.

How Portal Works

Portal is loaded as a script in your site:

<script src="{{asset "built/portal.js"}}" defer></script>

It provides:

  • Signup/sign-in modal
  • Account management modal
  • Tier selection interface
  • Payment processing via Stripe

Data Attributes

Control Portal with HTML data attributes:

<!-- Signup button -->
<button data-portal="signup">Subscribe</button>

<!-- Signup with specific tier -->
<button data-portal="signup/monthly">Monthly Plan</button>
<button data-portal="signup/yearly">Yearly Plan</button>

<!-- Login -->
<button data-portal="signin">Sign In</button>

<!-- Account management -->
<button data-portal="account">My Account</button>

<!-- Signout -->
<a data-portal="signout">Sign Out</a>

Portal Events

Listen for Portal events in JavaScript:

// Fired when Portal modal is opened
window.addEventListener('portal:open', () => {
  console.log('Portal opened');
});

// Fired when Portal modal is closed
window.addEventListener('portal:close', () => {
  console.log('Portal closed');
});

// Fired when member signs up
window.addEventListener('portal:signup', (event) => {
  console.log('New member signed up:', event.detail);
});

Custom Signup Flows

Method 1: Inline Signup Form

Embed a signup form directly on any page:

<form class="gh-signin-form" method="post" action="/members/api/send-magic-link/">
  <input type="hidden" name="redirect" value="/welcome/">
  <input type="email" name="email" placeholder="your@email.com" required>
  <button type="submit">Send magic link</button>
</form>

This sends a magic link to the email. When clicked, the member is logged in.

Method 2: Custom Registration Form

Combine the magic link approach with a custom redirect flow:

<form id="custom-signup">
  <input type="text" name="name" placeholder="Your name" required>
  <input type="email" name="email" placeholder="your@email.com" required>
  <select name="tier">
    <option value="free">Free</option>
    <option value="monthly">Monthly - $9/mo</option>
    <option value="yearly">Yearly - $90/yr</option>
  </select>
  <button type="submit">Join</button>
</form>

<script>
  document.getElementById('custom-signup').addEventListener('submit', async (e) => {
    e.preventDefault();
    const form = e.target;

    // For free tier: send magic link
    if (form.tier.value === 'free') {
      await fetch('/members/api/send-magic-link/', {
        method: 'POST',
        headers: { 'Content-Type': 'application/json' },
        body: JSON.stringify({
          email: form.email.value,
          redirect: '/welcome/'
        })
      });
      alert('Check your email for the sign-in link!');
    } else {
      // For paid tier: open Portal with selected tier
      window.location.href = `/#/portal/signup/${form.tier.value}`;
    }
  });
</script>

Admin API for Member Management

The Admin API lets you manage members programmatically. You need an Admin API key.

Authentication

// Create an Admin API client
const GhostAdminAPI = require('@tryghost/admin-api');

const api = new GhostAdminAPI({
  url: 'https://yoursite.com',
  key: 'YOUR_ADMIN_API_KEY',
  version: 'v5.0'
});

Creating Members

// Create a new member
const member = await api.members.add({
  name: 'John Doe',
  email: 'john@example.com',
  note: 'Signed up from custom page',
  labels: ['custom-signup', 'source-website']
});

console.log('Created member:', member.id);

Updating Members

// Update a member's tier or labels
const updated = await api.members.edit({
  id: memberId,
  labels: ['vip', 'annual']
});

Listing Members

// Get all members with filtering
const members = await api.members.browse({
  filter: 'status:paid',
  limit: 100
});

// Search for a member
const found = await api.members.browse({
  search: 'john@example.com'
});

Deleting Members

await api.members.delete({ id: memberId });

Ghost uses magic link authentication — members log in by clicking a link sent to their email.

  1. Member enters their email.
  2. Ghost generates a one-time login token.
  3. Ghost sends an email with a link containing the token.
  4. Member clicks the link and is authenticated.
// Send magic link
const response = await fetch('/members/api/send-magic-link/', {
  method: 'POST',
  headers: { 'Content-Type': 'application/json' },
  body: JSON.stringify({
    email: 'john@example.com',
    redirect: '/dashboard/'
  })
});

Programmatic Authentication

For server-side authentication, use the Identity API:

// Exchange member identity token for member info
const identity = await fetch('/ghost/api/v5/admin/authentication/identity/', {
  headers: {
    'Authorization': 'Ghost YYYYOURADMINKEY'
  }
}).then(r => r.json());

// Use identity token to get member info
const member = await fetch(`https://yoursite.com/ghost/api/v5/members/${identity}`, {
  headers: {
    'Authorization': 'Ghost YYYOURADMINKEY'
  }
}).then(r => r.json());

Webhooks for Member Events

Set up webhooks to react to member signups and subscription changes.

Webhook Events

Event When It Fires
member.added A new member is created
member.deleted A member is deleted
member.edited A member's profile is updated
subscription.created A new subscription is created
subscription.updated A subscription changes
subscription.deleted A subscription is canceled

Webhook Payload

{
  "member": {
    "current": {
      "id": "64a1b2c3d4e5f6",
      "name": "John Doe",
      "email": "john@example.com"
    }
  }
}

Setting Up Webhooks

  1. Go to Settings > Integrations > Add custom integration.
  2. Add a webhook with the URL of your endpoint.
  3. Select the events to listen for.
  4. Save.

Your endpoint receives POST requests with the member data.

Custom Account Dashboard

You can build a custom member dashboard that replaces or supplements the Portal.

<!-- Member dashboard template -->
<div class="member-dashboard">
  <h1>Welcome, {{@member.name}}</h1>

  <div class="dashboard-grid">
    <div class="dashboard-card">
      <h3>Subscription</h3>
      {{#if @member.paid}}
        <p>You are a paid subscriber.</p>
        <a href="#" data-portal="account">Manage</a>
      {{else}}
        <p>You are on the free plan.</p>
        <a href="#" data-portal="signup/monthly">Upgrade</a>
      {{/if}}
    </div>

    <div class="dashboard-card">
      <h3>Saved Content</h3>
      <p>View your bookmarked posts.</p>
    </div>

    <div class="dashboard-card">
      <h3>Settings</h3>
      <p>Update your profile and preferences.</p>
      <a href="#" data-portal="account">Edit profile</a>
    </div>
  </div>
</div>

Third-Party Integration

Connect Ghost memberships with external services.

Zapier Integration

  1. In Ghost admin, go to Integrations > Zapier.
  2. Copy the webhook URL.
  3. In Zapier, create a Zap with Ghost as the trigger.
  4. Choose "Member Added" as the trigger event.
  5. Send the data to your destination (Google Sheets, Mailchimp, Slack, etc.).

Custom Sync Script

Use the Admin API to sync members with an external database:

// Sync Ghost members with external CRM
async function syncMembersToCRM() {
  const members = await api.members.browse({ limit: 'all' });

  for (const member of members) {
    await fetch('https://mycrm.com/api/contacts', {
      method: 'POST',
      headers: { 'Content-Type': 'application/json' },
      body: JSON.stringify({
        email: member.email,
        name: member.name,
        source: 'ghost-membership',
        subscription: member.tiers?.[0]?.name
      })
    });
  }
}

Common Mistakes

  1. Building a custom signup flow for paid tiers from scratch: Stripe integration is complex and security-sensitive. For paid tiers, use the Ghost Portal which handles Stripe payment processing securely. Build custom flows only for free tier signups.

  2. Exposing Admin API keys in client-side code: Admin API keys have full access to your Ghost data. Never include them in browser JavaScript. Use them only in server-side code or secure backend functions.

  3. Not handling magic link failures gracefully: Magic links expire and can fail. Always show clear error messages when email delivery fails, and provide alternative signup methods.

  4. Forgetting to verify email delivery for magic links: If your email configuration is broken, magic links never arrive. Test email delivery thoroughly before launching a custom signup flow.

  5. Creating duplicate members via the API: When creating members programmatically, always check if the member already exists by email before creating a new one.

Practice Questions

  1. What is the Ghost Portal and how does it handle member authentication? Answer: The Portal is a JavaScript module that provides the member-facing signup, login, and account management UI. It uses magic link authentication — members enter their email and receive a one-time login link. The Portal handles Stripe payment processing for paid tier signups.

  2. How do you create a custom signup form for free tier members? Answer: Create an HTML form that POSTs to /members/api/send-magic-link/ with the email and a redirect URL. Ghost sends a magic link to that email. When the link is clicked, the member is authenticated. For paid tiers, use the Portal's data-portal attributes.

  3. What member events can you subscribe to via webhooks? Answer: member.added, member.deleted, member.edited, subscription.created, subscription.updated, subscription.deleted. These fire when members are created/deleted/updated and when subscriptions start/change/end.

  4. Challenge: Build a custom member signup flow for a headless Ghost site. Create a React component that: captures email and name, sends a magic link for free tier signup, opens the Ghost Portal for paid tier signup, handles errors gracefully, and redirects to a custom welcome page after signup.

FAQ

Can I use Ghost's membership system without the Portal?

Yes. You can build custom signup flows using the magic link API and Stripe Elements for payment processing. However, the Portal handles many edge cases and security concerns that you would need to implement yourself.

How do I authenticate members on a separate frontend (React/Next.js)?

Use the Ghost Content API with member tokens. When a member logs in via Portal, Ghost sets a cookie. For custom frontends, use the Identity API to get a member token and pass it to your frontend.

Can I charge different prices in different currencies?

Ghost uses a single currency set in Stripe. For multi-currency support, you would need to create multiple tiers in different currencies (each tier with a different Stripe price ID).

How do I programmatically cancel a member's subscription?

Use the Admin API: api.members.edit({id, subscriptions: [{id: subId, status: 'canceled'}]}). Or cancel in Stripe Dashboard and Ghost syncs the change via webhooks.

Can I require members to verify their email before accessing content?

Ghost does not have email verification for members. Magic link authentication serves as implicit verification — if the email is deliverable, the member receives the link. For added verification, use a custom signup flow with a verification step.

Mini Project

Your task: Build a custom member onboarding system.

  1. Create a custom landing page with a signup form that captures name and email.
  2. On form submit, send a magic link to the email via Ghost's API.
  3. After the member signs in (magic link), redirect to a custom welcome dashboard.
  4. The dashboard shows the member's name, subscription status, and links to account management.
  5. Add a "member created" webhook that logs new members to a Google Sheet.
  6. Test the full flow from signup to dashboard.

This exercise teaches you to extend Ghost memberships with custom code while leveraging Ghost's built-in authentication and member management.

What's Next

Now that you understand the Membership API, explore the Content API:

Continue to Lesson 25: Content API — Query posts, pages, tags, and authors via the REST API with filtering and pagination.

Related lessons:

Built by the developers of DodaTech

Doda Browser, DodaZIP & Durga Antivirus Pro