For your website

Plug DMA Connect into the new Next.js site

Written for the developer building dermatologymanagersassociation.org. The website stays the front door; DMA Connect is the system of record behind it. Four integration points cover everything MemberClicks does today with its website builder, without locking the site into the platform.

Hosted at connect.dermatologymanagersassociation.orgAllowed embed domains: dermatologymanagersassociation.orgAPI version 2026-09-01

Hosted login and SSO handoff

The website never handles passwords. A Sign in link sends the visitor to the hosted login page and DMA Connect sends them back to where they were going.

  1. Link to https://connect.dermatologymanagersassociation.org/login with a next parameter for the portal page to land on.
  2. DMA Connect authenticates (email and password, magic link or Google Workspace for staff) and sets a session on its own domain.
  3. If the website needs to know the visitor is a member (for example to unlock a members-only page), call POST /api/v1/members/verify server-side, or read the signed dma_session cookie shared across the .dermatologymanagersassociation.org parent domain.
Plain HTML link
<!-- Anywhere on the marketing site -->
<a href="https://connect.dermatologymanagersassociation.org/login?next=/portal">Member sign in</a>

<!-- Deep link straight to a portal page after login -->
<a href="https://connect.dermatologymanagersassociation.org/login?next=/portal/registrations">My registrations</a>
Next.js component
// app/components/member-button.tsx (on the website)
import Link from "next/link";

const CONNECT = process.env.NEXT_PUBLIC_DMA_CONNECT_URL; // https://connect.dermatologymanagersassociation.org

export function MemberButton({ next = "/portal" }: { next?: string }) {
  return (
    <Link href={`${CONNECT}/login?next=${encodeURIComponent(next)}`} className="btn btn-primary">
      Member sign in
    </Link>
  );
}
Try the handoff in this demo:/login?next=/portal/registrations

Embeddable widgets

Two drop-in widgets cover the pages the website needs most: an upcoming events list and a join call-to-action. Both read live data, inherit DMA branding and open the full flow in a new tab.

Option A: script tag (recommended)

Renders inline, sizes itself and picks up the page font. Attributes control the widget type, limit and event kind.

events widget
<!-- 1. Drop a container where the widget should render -->
<div data-dma-widget="events" data-limit="5" data-kind="all"></div>

<!-- 2. Load the embed script once, before </body> -->
<script async src="https://connect.dermatologymanagersassociation.org/embed.js"></script>

Option B: iframe

Zero JavaScript on the host page. Works in any CMS block that accepts raw HTML.

/embed/events
<iframe
  src="https://connect.dermatologymanagersassociation.org/embed/events"
  title="Upcoming DMA events"
  width="100%"
  height="520"
  style="border:0;border-radius:12px"
  loading="lazy"
></iframe>
/embed/join
<iframe
  src="https://connect.dermatologymanagersassociation.org/embed/join"
  title="Join DMA"
  width="100%"
  height="420"
  style="border:0;border-radius:12px"
  loading="lazy"
></iframe>
Live preview: /embed/eventsOpen
Live preview: /embed/joinOpen

Prefer to own the markup? Render the events page as a server component on the website and call the API directly. Cached for five minutes with Next.js revalidate.

app/events/page.tsx on the website
// app/events/page.tsx (on the website): server component, no client JS needed
export default async function EventsPage() {
  const res = await fetch("https://connect.dermatologymanagersassociation.org/api/v1/events", { next: { revalidate: 300 } });
  const { data } = await res.json();
  return (
    <ul>
      {data.map((ev) => (
        <li key={ev.id}>
          <a href={ev.url}>{ev.name}</a> · {ev.startsOn}
          {ev.ticketSummary.memberFrom !== null && <> · members from ${ev.ticketSummary.memberFrom}</>}
        </li>
      ))}
    </ul>
  );
}

REST API reference

Read-only JSON over HTTPS. Public endpoints need no key. Member verification takes the public API key from Admin, Website integration. All responses include permissive CORS headers so they can be called from the browser as well as the server.

MethodPathReturnsAuth
GET/api/v1/eventsPublished public events, upcoming first. Filters: kind, upcoming=false, limit.None
GET/api/v1/events/[slug]One event with tickets, agenda, speakers, sponsors and hotel info. 404 JSON if missing.None
GET/api/v1/membership-typesActive membership levels with price, term, benefits and a join URL.None
POST/api/v1/members/verify{ member, status, memberNumber?, expiresOn?, membershipTypeId? } for an email. No other personal data.X-DMA-Api-Key

Unsupported methods return 405 with an Allow header. Errors use { "error": { "code", "message" } }. Rate limit: 600 requests per minute per key or IP.

GET/api/v1/events
Request
curl https://connect.dermatologymanagersassociation.org/api/v1/events
Response
{
  "data": [
    {
      "id": "ev_webinar_oct",
      "slug": "manager-roundtable-2027-fee-schedule",
      "name": "Manager Roundtable: 2027 Fee Schedule & Coding Updates",
      "tagline": "What the proposed 2027 physician fee schedule means for dermatology reimbursement.",
      "kind": "webinar",
      "status": "published",
      "startsOn": "2026-10-14",
      "endsOn": "2026-10-14",
      "timezone": "America/New_York",
      "venueName": null,
      "venueCity": null,
      "isVirtual": true,
      "ceuCredits": 1,
      "capacity": 500,
      "registration": { "open": true, "opensOn": "2026-09-10", "closesOn": "2026-10-14" },
      "ticketSummary": { "count": 2, "memberFrom": 0, "nonMemberFrom": 49, "earlyBirdEndsOn": null, "currency": "USD" },
      "url": "https://connect.dermatologymanagersassociation.org/events/manager-roundtable-2027-fee-schedule",
      "registrationUrl": "https://connect.dermatologymanagersassociation.org/events/manager-roundtable-2027-fee-schedule/register"
    },
    {
      "id": "ev_webinar_nov",
      "slug": "year-end-hr-compliance-checklist",
      "name": "Q4 Roundtable: Year-End HR & Compliance Checklist",
      "kind": "webinar",
      "startsOn": "2026-11-12",
      "endsOn": "2026-11-12",
      "isVirtual": true,
      "ceuCredits": 1,
      "registration": { "open": false, "opensOn": "2026-10-01", "closesOn": "2026-11-12" },
      "ticketSummary": { "count": 2, "memberFrom": 0, "nonMemberFrom": 49, "earlyBirdEndsOn": null, "currency": "USD" },
      "registrationUrl": "https://connect.dermatologymanagersassociation.org/events/year-end-hr-compliance-checklist/register"
    },
    {
      "id": "ev_workshop_dec",
      "slug": "aesthetic-marketing-workshop",
      "name": "Aesthetic Marketing Workshop",
      "kind": "workshop",
      "startsOn": "2026-12-03",
      "endsOn": "2026-12-03",
      "isVirtual": true,
      "ceuCredits": 3,
      "registration": { "open": false, "opensOn": "2026-10-15", "closesOn": "2026-12-01" },
      "ticketSummary": { "count": 2, "memberFrom": 79, "nonMemberFrom": 129, "earlyBirdEndsOn": null, "currency": "USD" },
      "registrationUrl": "https://connect.dermatologymanagersassociation.org/events/aesthetic-marketing-workshop/register"
    },
    {
      "id": "ev_conf_2027",
      "slug": "2027-annual-conference",
      "name": "2027 DMA Annual Conference",
      "kind": "conference",
      "startsOn": "2027-04-16",
      "endsOn": "2027-04-17",
      "venueName": "The Hotel at Avalon, Autograph Collection",
      "venueCity": "Alpharetta, GA",
      "isVirtual": false,
      "ceuCredits": 12,
      "capacity": 320,
      "registration": { "open": true, "opensOn": "2026-09-15", "closesOn": "2027-04-10" },
      "ticketSummary": { "count": 6, "memberFrom": 195, "nonMemberFrom": 355, "earlyBirdEndsOn": "2027-02-15", "currency": "USD" },
      "registrationUrl": "https://connect.dermatologymanagersassociation.org/events/2027-annual-conference/register"
    }
  ],
  "meta": { "count": 4, "asOf": "2026-09-29", "upcomingOnly": true }
}
GET/api/v1/events/[slug]
Request
curl https://connect.dermatologymanagersassociation.org/api/v1/events/2027-annual-conference
Response
{
  "data": {
    "id": "ev_conf_2027",
    "slug": "2027-annual-conference",
    "name": "2027 DMA Annual Conference",
    "tagline": "Transforming the landscape of education and networking for dermatology and aesthetic practice teams.",
    "kind": "conference",
    "status": "published",
    "startsOn": "2027-04-16",
    "endsOn": "2027-04-17",
    "timezone": "America/New_York",
    "venueName": "The Hotel at Avalon, Autograph Collection",
    "venueCity": "Alpharetta, GA",
    "isVirtual": false,
    "ceuCredits": 12,
    "capacity": 320,
    "registration": { "open": true, "opensOn": "2026-09-15", "closesOn": "2027-04-10" },
    "ticketSummary": { "count": 6, "memberFrom": 195, "nonMemberFrom": 355, "earlyBirdEndsOn": "2027-02-15", "currency": "USD" },
    "url": "https://connect.dermatologymanagersassociation.org/events/2027-annual-conference",
    "registrationUrl": "https://connect.dermatologymanagersassociation.org/events/2027-annual-conference/register",
    "description": "Two days of practical education for the people who run dermatology, plastic surgery and medical aesthetic practices. ...",
    "heroColor": "#12385e",
    "tracks": ["Managers", "Aesthetic", "Front & Back Office"],
    "venueAddress": { "line1": "9000 Avalon Blvd", "city": "Alpharetta", "state": "GA", "zip": "30009" },
    "hotelInfo": "A discounted room block at The Hotel at Avalon is available until March 20, 2027 at $219/night. Use group code DMA27 when booking.",
    "tickets": [
      { "id": "tt_2027_mgr_member", "name": "Managers Track - Member", "description": "Friday and Saturday sessions, meals and reception.", "memberOnly": true, "track": "Managers", "price": 345, "earlyBird": true, "listPrice": 395, "earlyBirdEndsOn": "2027-02-15", "capacity": 180 },
      { "id": "tt_2027_mgr_nonmember", "name": "Managers Track - Non-member", "description": "Friday and Saturday sessions, meals and reception.", "memberOnly": false, "track": "Managers", "price": 495, "earlyBird": true, "listPrice": 545, "earlyBirdEndsOn": "2027-02-15", "capacity": 60 }
    ],
    "sessions": [
      { "id": "s27_1", "title": "Welcome & State of the Association", "description": null, "track": "General", "day": "2027-04-16", "startTime": "08:30", "endTime": "09:00", "room": "Avalon Ballroom", "ceuCredits": 0, "speakers": ["Grant Kim"] },
      { "id": "s27_2", "title": "Mastering Insurance Denials & the Appeals Process, 2027 Edition", "description": "Denial trends, payer-specific appeal strategies and the KPIs a manager should review weekly.", "track": "Managers", "day": "2027-04-16", "startTime": "09:00", "endTime": "10:15", "room": "Avalon Ballroom A", "ceuCredits": 1.25, "speakers": ["Kendra Wallace, CPC"] }
    ],
    "speakers": [{ "id": "sp_2", "name": "Kendra Wallace, CPC", "title": "Revenue Cycle Consultant", "company": "ClearClaim Consulting" }],
    "sponsors": [{ "id": "sponsor_galderma", "name": "Galderma", "tier": "Platinum", "website": "https://www.galderma.com", "category": "Pharma", "boothNumber": "1" }],
    "customQuestions": [
      { "id": "q_role", "label": "Primary role", "type": "select", "required": true, "options": ["Administrator / Practice Manager", "Office Manager", "Billing / Insurance", "Aesthetic staff", "Provider", "Vendor"] },
      { "id": "q_first", "label": "Is this your first DMA conference?", "type": "radio", "required": true, "options": ["Yes", "No"] }
    ]
  }
}
GET/api/v1/membership-types
Request
curl https://connect.dermatologymanagersassociation.org/api/v1/membership-types
Response
{
  "data": [
    {
      "id": "mt_manager",
      "slug": "manager",
      "name": "Manager Membership",
      "description": "For administrators, practice managers and key personnel with direct management responsibility in a dermatology, plastic surgery or medical aesthetic practice.",
      "price": 150,
      "currency": "USD",
      "term": "annual",
      "gracePeriodDays": 30,
      "autoRenewDefault": true,
      "maxSeats": null,
      "benefits": [
        "Discounted registration to the DMA Annual Conference",
        "Member-only resource library and conference recordings",
        "Access to the DMA member directory and community forum",
        "Monthly manager roundtable webinars with CEUs",
        "Job board posting and resume bank",
        "Salary and benchmark survey results"
      ],
      "joinUrl": "https://connect.dermatologymanagersassociation.org/join?type=manager"
    },
    { "id": "mt_practice", "slug": "practice", "name": "Practice Membership", "price": 425, "term": "annual", "maxSeats": 5, "joinUrl": "https://connect.dermatologymanagersassociation.org/join?type=practice" },
    { "id": "mt_aesthetic", "slug": "aesthetic", "name": "Aesthetic Staff Membership", "price": 95, "term": "annual", "maxSeats": null, "joinUrl": "https://connect.dermatologymanagersassociation.org/join?type=aesthetic" },
    { "id": "mt_partner", "slug": "industry-partner", "name": "Industry Partner", "price": 650, "term": "annual", "maxSeats": null, "joinUrl": "https://connect.dermatologymanagersassociation.org/join?type=industry-partner" }
  ],
  "meta": { "count": 4 }
}
POST/api/v1/members/verify
Request
curl -X POST https://connect.dermatologymanagersassociation.org/api/v1/members/verify \
  -H "Content-Type: application/json" \
  -H "X-DMA-Api-Key: dma_pk_live_..." \
  -d '{ "email": "melissa.carter@peachtreederm.com" }'
Response
{
  "member": true,
  "status": "active",
  "memberNumber": "DMA-2019-0135",
  "expiresOn": "2027-03-15",
  "membershipTypeId": "mt_practice"
}

// Unknown email
{ "member": false, "status": "not-found" }

Webhooks

DMA Connect posts a signed JSON event to the website whenever something the site cares about changes. Retries with backoff for 24 hours on non-2xx responses. The delivery log lives in the admin console.

EventWhen it firesPayload data
member.createdA membership application is approved and the member record goes live.memberId, memberNumber, membershipTypeId, joinedOn
member.renewedDues are paid and the expiry date moves forward, including auto-renewals.memberId, membershipTypeId, expiresOn, invoiceId
registration.createdSomeone registers for an event, member or guest, paid or free.registrationId, eventId, eventSlug, ticketTypeId, memberId?, amountPaid, confirmationCode
invoice.paidAny invoice is settled: dues, event tickets or sponsorships.invoiceId, number, kind, memberId, total, paymentMethod, paidOn
Example delivery
POST https://www.dermatologymanagersassociation.org/api/dma-webhook
Content-Type: application/json
X-DMA-Signature: t=1759152000,v1=5f1e0a6c...   (HMAC-SHA256 of the raw body with your signing secret)

{
  "id": "evt_01J9Q0X8K2",
  "type": "registration.created",
  "createdAt": "2026-09-29T14:12:08Z",
  "data": {
    "registrationId": "reg_01J9Q0X8",
    "eventId": "ev_conf_2027",
    "eventSlug": "2027-annual-conference",
    "ticketTypeId": "tt_2027_mgr_member",
    "memberId": "m_001",
    "amountPaid": 345,
    "confirmationCode": "DMA-7KQ2ZR"
  }
}
Verify the signature on the website
// app/api/dma-webhook/route.ts (on the website)
import { createHmac, timingSafeEqual } from "node:crypto";

export async function POST(req: Request) {
  const raw = await req.text();
  const sig = req.headers.get("x-dma-signature") ?? "";
  const v1 = sig.split(",").find((p) => p.startsWith("v1="))?.slice(3) ?? "";
  const expected = createHmac("sha256", process.env.DMA_WEBHOOK_SECRET!).update(raw).digest("hex");
  if (!v1 || !timingSafeEqual(Buffer.from(v1), Buffer.from(expected))) {
    return new Response("bad signature", { status: 401 });
  }
  const event = JSON.parse(raw);
  switch (event.type) {
    case "member.created":       // add to the website newsletter list
    case "member.renewed":       // refresh cached member count on the home page
    case "registration.created": // bump the "spots left" counter
    case "invoice.paid":         // sync to QuickBooks
  }
  return new Response("ok");
}

Try it

These requests hit the route handlers running in this demo. They read the same seed data the rest of the app starts from.

Pick an endpoint and send a request. The response comes from this app's route handlers.

Shared branding

The portal, admin console, emails and widgets use the same design tokens as this page: navy #12385e, teal #149c8c, gold #c9862b, Inter for body text and Plus Jakarta Sans for headings. The website can import the same token file so a member never notices the seam between the two.

#12385e#149c8c#c9862b#0b2540#e6f5f2