Membership
For your group's own website

Website API and widgets

Show your executives, events, communiqués, committees and programmes on your own website, let visitors check a membership card and apply to join — straight from Membership, always up to date. Paste a widget, or call the API from your own code.

1 · Get a key

In your group's officers' console: Website & API → Create key.

The key is shown once. Revoke it from the same page at any time.

2 · Paste a widget

Add the script once, anywhere on the page, then a <div> wherever a widget should appear:

<div data-membership="executives" data-key="mo_pk_YOUR_PUBLIC_KEY"></div>
<script src="https://membergate.org/embed.js" defer></script>
data-membershipShows
groupName, tagline, about, and the join button
executivesThe current executive with photos and offices
past-executivesThe roll of past executives with their tenure
eventsUpcoming events with theme and a Register button when registration is open (guests get a QR entry pass)
communiquesThe latest communiqués; add data-full="true" to print the newest one in full
committeesCommittees and subcommittees, their purpose, chairman and members
programmesProgrammes and publicity that are running
noticesNotices and news that are running
verifyA box to check a membership card: name, status and whether dues are up to date
joinJust the "Apply for membership" button (data-label changes its text)

Options on any widget: data-limit="3", data-theme="dark", data-accent="#7A1F2B" (your colour), data-font="inherit" (use your site's font), data-credit="false" (hide "Powered by"). Widgets draw inside their own shadow root, so your site's styles and theirs never clash.

3 · Or call the API

Base address https://membergate.org/api/v1. Send the key as Authorization: Bearer mo_pk_…. Public keys also work from the browser on their allowed websites. Answers are JSON; times are ISO 8601 (UTC), dates YYYY-MM-DD.

RequestReturns
GET /groupProfile: name, tagline, about, history, founding date, contacts, logo, colours, join_url, portal_url
GET /executives{ current: [...], past: [...] } — office, name, photo_url, bio, starts_on, ends_on
GET /posts?kind=programme,noticeRunning programmes, publicity, notices and news
GET /eventsUpcoming public events: title, kind, theme, speakers, venue, starts_at, ends_at, register_url
GET /communiquesPublished communiqués: heading, event, speakers, theme, intro, resolutions[], signed, url
GET /communiques/{id}One communiqué
GET /committeesCommittees and subcommittees: name, kind, purpose, mandate, chair, members[] with roles
GET /directory?q=&type=businessThe members directory, when the group makes it public
GET /verify/{member number}{ found, full_name, member_number, status, category, member_since, dues_up_to_date }
// In a page (public key)
const res = await fetch('https://membergate.org/api/v1/executives', { headers: { Authorization: 'Bearer mo_pk_…' } });
const { current } = await res.json();
current.forEach((t) => console.log(t.office, t.name));

# From a terminal
curl -H "Authorization: Bearer mo_pk_…" https://membergate.org/api/v1/events
// GET /api/v1/events
[{ "id": "115c79db-…", "title": "Quarter 4 Business Breakfast Meeting", "kind": "business_breakfast",
   "theme": "Cape Town Convention: …", "speakers": ["Mrs. Olubunmi Kuku, Managing Director/Chief Executive, FAAN", "…"],
   "venue": "Radisson Blu, Ikeja GRA", "starts_at": "2026-11-19T07:00:00.000Z", "ends_at": "2026-11-19T11:00:00.000Z",
   "status": "scheduled", "register_url": "https://membergate.org/register/9f3c…" }]

4 · From your server (secret key)

RequestDoes
GET /members?status=active&updated_since=…The member register (number, name, contacts, category, status, card link); updated_since for syncing
GET /members/{member number}One member, with what they owe and whether their dues are up to date
POST /applicationsSends an application from your own form: full_name, email, phone, date_of_birth, category. It waits for approval in the officers' console like any other; the answer's next is the link to send the applicant to create their account
GET /directoryThe directory as members see it
POST /membersUp to 1,000 members at a time: { members: [{ full_name, email, phone, date_of_birth, category, chapters: [..], member_number, joined_at, address, fields: { "Label": value } }], mode: "invite" | "active" | "pending", dry_run, send_messages }. Every row is checked and the answer gives each row's result; with dry_run: true nothing is saved
GET /schemaThe columns a member row may carry for your group, with the categories, chapters and field choices
GET /template.xlsxThe group's Excel template
GET /imports/{id}An upload's rows and what happened to each
<?php // PHP: send an application from your site's own form
$ch = curl_init('https://membergate.org/api/v1/applications');
curl_setopt_array($ch, [CURLOPT_POST => true, CURLOPT_RETURNTRANSFER => true,
  CURLOPT_HTTPHEADER => ['Authorization: Bearer ' . getenv('MEMBERS_SECRET_KEY'), 'Content-Type: application/json'],
  CURLOPT_POSTFIELDS => json_encode(['full_name' => $_POST['name'], 'email' => $_POST['email'], 'phone' => $_POST['phone'], 'date_of_birth' => $_POST['dob']])]);
$answer = json_decode(curl_exec($ch), true);

Never put a secret key in a page, an app or a URL: requests with ?key=mo_sk_… are refused.

5 · Webhooks

Website & API → Webhooks: Membership POSTs to your address when something changes — member.joined, member.activated, member.updated, member.status, officer.changed, charge.created, payment.recorded, event.created, communique.published. A failed delivery is retried with growing gaps, up to 8 times.

POST https://your-site/hooks/members
X-Members-Event: communique.published
X-Members-Signature: hex(HMAC-SHA256(raw body, your webhook secret))

{ "event": "communique.published", "group_id": "…", "at": "2026-11-19T12:40:00.000Z", "data": { "id": "…", "heading": "…", "resolutions": ["…"] } }
// Node: check the signature before trusting the body
const ok = crypto.timingSafeEqual(Buffer.from(req.get('x-members-signature')),
  Buffer.from(crypto.createHmac('sha256', SECRET).update(rawBody).digest('hex')));

<?php // PHP
$ok = hash_equals(hash_hmac('sha256', file_get_contents('php://input'), $secret), $_SERVER['HTTP_X_MEMBERS_SIGNATURE'] ?? '');

6 · On WordPress, Wix and other site builders