Ghost Membership API — Custom Signup Flows and Portal Integration
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:
- Portal JavaScript SDK — Drop-in modal for signup, login, account management
- Admin API — Server-side member CRUD operations
- Webhooks — Real-time notifications for member events
- 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 });
Magic Link Authentication
Ghost uses magic link authentication — members log in by clicking a link sent to their email.
How Magic Links Work
- Member enters their email.
- Ghost generates a one-time login token.
- Ghost sends an email with a link containing the token.
- Member clicks the link and is authenticated.
API Endpoint for Magic Links
// 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
- Go to
Settings > Integrations > Add custom integration. - Add a webhook with the URL of your endpoint.
- Select the events to listen for.
- 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
- In Ghost admin, go to
Integrations > Zapier. - Copy the webhook URL.
- In Zapier, create a Zap with Ghost as the trigger.
- Choose "Member Added" as the trigger event.
- 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
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.
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.
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.
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.
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
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.
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.
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.
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
Mini Project
Your task: Build a custom member onboarding system.
- Create a custom landing page with a signup form that captures name and email.
- On form submit, send a magic link to the email via Ghost's API.
- After the member signs in (magic link), redirect to a custom welcome dashboard.
- The dashboard shows the member's name, subscription status, and links to account management.
- Add a "member created" webhook that logs new members to a Google Sheet.
- 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:
- Admin API — Admin-level API operations
- Headless Ghost — Use Ghost as a headless CMS
Built by the developers of DodaTech
Doda Browser, DodaZIP & Durga Antivirus Pro