Developer docs

REST API

Connect your organization's own systems to Melora Connect: read and update members, read events with their registrations, create draft events, read dues invoices and subscribe webhook URLs.

Overview

The API answers JSON over HTTPS. Every path on this page starts with this base URL:

https://app.meloraconnect.com/api/public/v1

To hear about changes as they happen, use webhooks. To use the same operations from an MCP client, see the MCP server.

Who can use it

The API is included in every Melora Connect plan. Keys are made in Melora Connect by staff who can manage the organization's settings. If the API is ever taken off the organization's plan, its keys stop working on the next request, and work again once the plan includes it, unless they were revoked or have expired.

Authentication

Every request carries one API key in the Authorization header, after the word Bearer:

Authorization: Bearer melora_YOUR_KEY

Make a key

  1. In Melora Connect, open Settings and then the API keys tab.
  2. Name the key after the system that will use it, choose its permissions and when it expires, then click Make key.
  3. Copy the key and store it on your server. It is shown only once.

About keys

  • A key belongs to one organization. Every request acts in that organization and no other, and nothing in a request can choose a different one. A record of another organization answers 404, as if it did not exist.
  • Melora Connect keeps only a fingerprint of the key (a SHA-256 hash), so it cannot show the key again. Store it like a password, on your server, never in a web page or a mobile app.
  • Keys start with melora_ followed by 43 characters. The API keys tab shows the first 15 characters of each key, so you can tell them apart, and when each was last used.
  • A key can expire after 30, 90 or 365 days, or never, and it can be revoked at any time with Revoke. A revoked or expired key is refused from the next request.
  • An organization can have up to 25 active keys (revoked and expired keys do not count). Make one key per system, so you can revoke one without touching the others.
  • A key's permissions are set when it is made. To change them, make a new key and revoke the old one.

Permissions

Each key carries only the permissions chosen for it, from the same permissions staff roles use in Melora Connect. The person making a key can only give it permissions they hold themselves. A request the key does not allow answers 403 with the code permission_denied.

Key permissionWhat it allows
members.readRead members: the roster and one member.
members.writeAdd members, change the listed member fields, and unsubscribe a member from the organization's marketing email.
events.readRead events, and one event with its registrations.
events.manageCreate events as drafts.
members.billing.readRead dues invoices.
settings.manageSubscribe and unsubscribe webhook URLs (what Zapier uses).

Versions

The version is in the path, /v1. Within v1, Melora Connect only adds: new endpoints, new optional parameters and new fields in answers, so your code should ignore fields it does not know. Anything that would break a working integration comes as a new version, with v1 kept running alongside it.

Limits

  • Each key can make 120 requests every 60 seconds. Past that, the answer is 429 with a Retry-After header in seconds.
  • Across all its keys, an organization can make 600 requests every 60 seconds. Past that, every key is answered 429 until the window moves on.
  • An address that presents more than 30 unknown keys in 10 minutes is answered 429 as well.
  • A request body is JSON, sent with Content-Type: application/json, and can be up to 64 KB. A larger one is answered 413 (payload_too_large) without being read.

Lists and formats

Every list takes page (from 1) and per_page (1 to 100, default 50) and answers with data, page, per_page and has_more. Ask for the next page while has_more is true.

Members come oldest change first. To keep a copy in step, pass the latest updated_at you hold as updated_since: records changed at that moment or later come back. Events come by start time, and dues invoices oldest first.

Times are ISO 8601 with a time zone (for example 2026-11-12T08:00:00-05:00), dates are YYYY-MM-DD, money is in cents, and ids are UUIDs. An id that is not a UUID answers 404.

Endpoints

Every endpoint, the permission its key needs and what it does. Each one is explained below with an example.

RequestRequired permissionWhat it does
GET /membersmembers.readList members, oldest change first.
GET /members/{id}members.readOne member.
POST /membersmembers.writeAdd a member. Answers 201 with the new member.
PATCH /members/{id}members.writeChange the fields you send of one member.
POST /members/{id}/email-preferencesmembers.writeUnsubscribe a member from the organization's marketing email, as the unsubscribe link in a campaign does. Answers 200.
GET /eventsevents.readList events by start time.
GET /events/{id}events.readOne event with its registrations.
POST /eventsevents.manageCreate an event as a draft. Answers 201 with the event.
GET /dues-invoicesmembers.billing.readList dues invoices, oldest first.
POST /webhookssettings.manageSubscribe a webhook URL to events. This is what Zapier uses.
DELETE /webhooks/{id}settings.manageUnsubscribe a webhook that was subscribed through the API.

GET /api/public/v1/members

List members, oldest change first.

Required permission: members.read

Parameters

  • status (optional): active, pending, lapsed, expired, suspended, archived or at_risk.
  • membership_type_id (optional): the id of one of the organization's membership types.
  • updated_since (optional): a date and time with a time zone. Only members changed at that moment or later.
  • email_marketing_opt_out (optional): true lists only the members who are opted out of the organization's marketing email now, false only the others.
  • page and per_page (optional): see Lists and formats.

Each member has the fields below. custom_fields holds the organization's own member fields, as field key and value.

email_marketing_opt_out is true when the member has opted out of the organization's marketing email, and email_marketing_opted_out_at is when (null while they have not). It is the reading Melora Connect's own campaigns use: an unsubscribe link or complaint from the address, for this organization or for all of Melora Connect, or the member's email switch for the organization turned off (also by a connected Mailchimp audience). A bounce is not an opt-out, and one organization's unsubscribe never opts the member out of another's. An opt-out does not change updated_at: follow the member.unsubscribed and member.resubscribed webhooks, and reconcile from time to time with email_marketing_opt_out=true.

Fields: id, name, first_name, last_name, email, company, title, industry, phone, website, linkedin_url, city, state, zip, bio, status, membership_type_id, membership_category, tier, join_date, renewal_date, tags, show_in_directory, chapter_id, created_at, updated_at, custom_fields, email_marketing_opt_out, email_marketing_opted_out_at.

Example request

curl "https://app.meloraconnect.com/api/public/v1/members?status=active&per_page=50&page=1" \
  -H "Authorization: Bearer $MELORA_API_KEY"

Example response, HTTP 200

{
  "data": [
    {
      "id": "8d2c6f0e-3f59-4a53-9c0e-0f6f5b3a1b21",
      "name": "Ana Ruiz",
      "first_name": "Ana",
      "last_name": "Ruiz",
      "email": "ana@example.org",
      "company": "Ruiz Consulting",
      "status": "active",
      "membership_type_id": "0b7a3c62-7f3e-4d7c-8a57-25a1c0a9d7e4",
      "join_date": "2026-03-02",
      "renewal_date": "2027-03-02",
      "tags": ["board"],
      "updated_at": "2026-09-28T14:03:11.402Z",
      "email_marketing_opt_out": false,
      "email_marketing_opted_out_at": null
    }
  ],
  "page": 1,
  "per_page": 50,
  "has_more": false
}

GET /api/public/v1/members/{id}

One member.

Required permission: members.read

Parameters

  • id (in the path): the member's id.

The member has the same fields as in the list. A member that does not exist, or belongs to another organization, answers 404.

Example request

curl "https://app.meloraconnect.com/api/public/v1/members/8d2c6f0e-3f59-4a53-9c0e-0f6f5b3a1b21" \
  -H "Authorization: Bearer $MELORA_API_KEY"

Example response, HTTP 200

{
  "data": {
    "id": "8d2c6f0e-3f59-4a53-9c0e-0f6f5b3a1b21",
    "name": "Ana Ruiz",
    "first_name": "Ana",
    "last_name": "Ruiz",
    "email": "ana@example.org",
    "company": "Ruiz Consulting",
    "status": "active",
    "membership_type_id": "0b7a3c62-7f3e-4d7c-8a57-25a1c0a9d7e4",
    "join_date": "2026-03-02",
    "renewal_date": "2027-03-02",
    "tags": [
      "board"
    ],
    "updated_at": "2026-09-28T14:03:11.402Z",
    "email_marketing_opt_out": false,
    "email_marketing_opted_out_at": null,
    "custom_fields": {
      "member_since_year": 2019
    }
  }
}

POST /api/public/v1/members

Add a member. Answers 201 with the new member.

Required permission: members.write

Parameters

  • email (required): one member per email address in the organization.
  • name, or first_name and last_name (required): at least one of them.
  • status (optional): active, the default, or pending, lapsed, expired, suspended or archived.
  • join_date and renewal_date (optional): dates as YYYY-MM-DD.
  • membership_type_id (optional): one of the organization's membership types.
  • Also optional: company, title, industry, phone, website, linkedin_url, city, state, zip, bio and tags (a list of words).
  • start_welcome_drips (optional): true to start the organization's New member campaigns for an active member.

An active member uses one of the plan's active member places; when none is left, the answer is 403 limit_reached. An email that already belongs to a member of the organization answers 409 conflict, with that member's id in details.existing_member_id.

A member added through the API joins none of the organization's automatic email campaigns, so a system that copies a whole roster in does not send everyone a welcome. To welcome a new member, send start_welcome_drips as true: an active member then joins the organization's New member campaigns, as a member added by hand does, and the answer carries welcome_drips.enrolled, the number of campaigns they joined (0 for a member who is not active).

phone must have 7 to 15 digits and is stored as its digits with any leading + kept (+1 (703) 555-0142 is stored as +17035550142). website must be an http or https address and is stored with its scheme (https:// is added when none is sent). linkedin_url must be an https address on linkedin.com. An empty value clears a field. An invalid value answers 422 validation_failed, and details names the field.

Example request

curl -X POST "https://app.meloraconnect.com/api/public/v1/members" \
  -H "Authorization: Bearer $MELORA_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"first_name":"Ana","last_name":"Ruiz","email":"ana@example.org","company":"Ruiz Consulting","status":"active","renewal_date":"2027-03-02","tags":["board"]}'

Example response, HTTP 201

{
  "data": {
    "id": "8d2c6f0e-3f59-4a53-9c0e-0f6f5b3a1b21",
    "name": "Ana Ruiz",
    "first_name": "Ana",
    "last_name": "Ruiz",
    "email": "ana@example.org",
    "company": "Ruiz Consulting",
    "status": "active",
    "membership_type_id": "0b7a3c62-7f3e-4d7c-8a57-25a1c0a9d7e4",
    "join_date": "2026-03-02",
    "renewal_date": "2027-03-02",
    "tags": [
      "board"
    ],
    "updated_at": "2026-09-28T14:03:11.402Z",
    "email_marketing_opt_out": false,
    "email_marketing_opted_out_at": null,
    "custom_fields": {}
  }
}

PATCH /api/public/v1/members/{id}

Change the fields you send of one member.

Required permission: members.write

Parameters

  • id (in the path): the member's id.
  • Body: any of name, first_name, last_name, email, company, title, industry, phone, website, linkedin_url, city, state, zip, bio, tags, membership_type_id, status and renewal_date. Send at least one.

Any other field is refused with 422. The at_risk status is worked out by Melora Connect from the member's activity and cannot be set, and points, engagement and sign-in figures cannot be changed. membership_type_id must be one of the organization's membership types.

Making a member active uses one of the plan's active member places. If the member's status changed while your request ran, the answer is 409 conflict: read the member and try again.

phone, website and linkedin_url are checked as when adding a member. Send only the fields you change: an older record may hold a phone number that fails today's checks, so sending the whole record back can be refused.

Example request

curl -X PATCH "https://app.meloraconnect.com/api/public/v1/members/8d2c6f0e-3f59-4a53-9c0e-0f6f5b3a1b21" \
  -H "Authorization: Bearer $MELORA_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"phone":"+1 555 0100","status":"lapsed"}'

Example response, HTTP 200

{
  "data": {
    "id": "8d2c6f0e-3f59-4a53-9c0e-0f6f5b3a1b21",
    "name": "Ana Ruiz",
    "first_name": "Ana",
    "last_name": "Ruiz",
    "email": "ana@example.org",
    "company": "Ruiz Consulting",
    "status": "lapsed",
    "membership_type_id": "0b7a3c62-7f3e-4d7c-8a57-25a1c0a9d7e4",
    "join_date": "2026-03-02",
    "renewal_date": "2027-03-02",
    "tags": [
      "board"
    ],
    "updated_at": "2026-10-04T10:12:45.118Z",
    "email_marketing_opt_out": false,
    "email_marketing_opted_out_at": null,
    "phone": "+15550100"
  }
}

POST /api/public/v1/members/{id}/email-preferences

Unsubscribe a member from the organization's marketing email, as the unsubscribe link in a campaign does. Answers 200.

Required permission: members.write

Parameters

  • id (in the path): the member's id.
  • Body: email_marketing_opt_out (required), true. Any other field is refused with 422.

The organization's next campaign leaves the member out, their email switch for the organization is turned off, and they leave its newsletter lists. Other organizations' email is not touched. The answer carries member_id, email_marketing_opt_out and email_marketing_opted_out_at; asking again for a member who is already opted out changes nothing. The change is in the organization's audit log with the key's name. A member without an email address answers 422.

A key cannot resubscribe a member: false answers 422 validation_failed. Only the member can turn the organization's email back on, in Melora Connect, and member.resubscribed is then sent.

Example request

curl -X POST "https://app.meloraconnect.com/api/public/v1/members/8d2c6f0e-3f59-4a53-9c0e-0f6f5b3a1b21/email-preferences" \
  -H "Authorization: Bearer $MELORA_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"email_marketing_opt_out":true}'

Example response, HTTP 200

{
  "data": {
    "member_id": "8d2c6f0e-3f59-4a53-9c0e-0f6f5b3a1b21",
    "email_marketing_opt_out": true,
    "email_marketing_opted_out_at": "2026-11-05T09:12:44.120Z"
  }
}

GET /api/public/v1/events

List events by start time.

Required permission: events.read

Parameters

  • status (optional): draft, published, cancelled, completed or archived.
  • starts_after (optional): a date and time with a time zone. Only events that start then or later.
  • updated_since (optional): only events changed at that moment or later.
  • page and per_page (optional): see Lists and formats.

Each event has the fields below. Prices are in cents.

Fields: id, title, description, event_type, status, starts_at, ends_at, timezone, venue, address, is_virtual, capacity, ticket_price_cents, member_price_cents, highlights, slug, published_at, created_at, updated_at, tags.

Example request

curl "https://app.meloraconnect.com/api/public/v1/events?starts_after=2026-11-01T00:00:00Z&per_page=20" \
  -H "Authorization: Bearer $MELORA_API_KEY"

Example response, HTTP 200

{
  "data": [
    {
      "id": "5e0c1d8a-9b2f-4c1e-8f3a-6a7b8c9d0e1f",
      "title": "Autumn networking breakfast",
      "event_type": "networking",
      "status": "published",
      "starts_at": "2026-11-12T13:00:00+00:00",
      "ends_at": "2026-11-12T14:30:00+00:00",
      "timezone": "America/New_York",
      "venue": "Harbor Hall",
      "is_virtual": false,
      "capacity": 80,
      "ticket_price_cents": 2500,
      "member_price_cents": 1500,
      "published_at": "2026-10-01T15:00:00+00:00"
    }
  ],
  "page": 1,
  "per_page": 20,
  "has_more": false
}

GET /api/public/v1/events/{id}

One event with its registrations.

Required permission: events.read

Parameters

  • id (in the path): the event's id.

The event's fields, plus host_committee_name (the committee hosting it, or null) and registrations, each with the fields below. member_id in a registration is the member record of the person registered, or null for a guest.

Fields: id, status, member_id, guest_name, guest_email, ticket_tier_id, checked_in_at, created_at.

Example request

curl "https://app.meloraconnect.com/api/public/v1/events/5e0c1d8a-9b2f-4c1e-8f3a-6a7b8c9d0e1f" \
  -H "Authorization: Bearer $MELORA_API_KEY"

Example response, HTTP 200

{
  "data": {
    "id": "5e0c1d8a-9b2f-4c1e-8f3a-6a7b8c9d0e1f",
    "title": "Autumn networking breakfast",
    "status": "published",
    "starts_at": "2026-11-12T13:00:00+00:00",
    "capacity": 80,
    "registrations": [
      {
        "id": "a1b2c3d4-0000-4000-8000-000000000001",
        "status": "confirmed",
        "member_id": "8d2c6f0e-3f59-4a53-9c0e-0f6f5b3a1b21",
        "guest_name": null,
        "guest_email": null,
        "ticket_tier_id": null,
        "checked_in_at": null,
        "created_at": "2026-10-02T15:20:00+00:00"
      }
    ]
  }
}

POST /api/public/v1/events

Create an event as a draft. Answers 201 with the event.

Required permission: events.manage

Parameters

  • title (required): 2 to 120 characters.
  • event_type (required): the kind of event, for example networking.
  • format (required): in_person, virtual or hybrid.
  • starts_at (required) and ends_at (optional): dates and times with a time zone.
  • timezone (optional): a time zone name such as America/New_York, which is the default.
  • ticket_price_cents and member_price_cents (optional): prices in cents, 0 by default.
  • capacity (optional): the number of places.
  • Also optional: description, venue, address, highlights, tags (a list of words) and language (en, es or fr).

The event is saved as a draft. Staff review and publish it in Melora Connect, and add the join link of a virtual event there. If events are not part of the organization's plan, the answer is 403 plan_required.

Example request

curl -X POST "https://app.meloraconnect.com/api/public/v1/events" \
  -H "Authorization: Bearer $MELORA_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"title":"Autumn networking breakfast","event_type":"networking","format":"in_person","starts_at":"2026-11-12T08:00:00-05:00","ends_at":"2026-11-12T09:30:00-05:00","timezone":"America/New_York","venue":"Harbor Hall","capacity":80,"ticket_price_cents":2500,"member_price_cents":1500}'

Example response, HTTP 201

{
  "data": {
    "id": "5e0c1d8a-9b2f-4c1e-8f3a-6a7b8c9d0e1f",
    "title": "Autumn networking breakfast",
    "event_type": "networking",
    "status": "draft",
    "starts_at": "2026-11-12T13:00:00+00:00",
    "ends_at": "2026-11-12T14:30:00+00:00",
    "timezone": "America/New_York",
    "venue": "Harbor Hall",
    "is_virtual": false,
    "capacity": 80,
    "ticket_price_cents": 2500,
    "member_price_cents": 1500,
    "published_at": null
  }
}

GET /api/public/v1/dues-invoices

List dues invoices, oldest first.

Required permission: members.billing.read

Parameters

  • status (optional): open, paid, past_due, void, refunded or failed.
  • member_id (optional): only this member's invoices.
  • updated_since (optional): only invoices changed at that moment or later.
  • page and per_page (optional): see Lists and formats.

Each invoice has the fields below. Amounts are in cents. Invoices are read only: payments happen in Melora Connect.

Fields: id, invoice_number, member_id, dues_subscription_id, status, amount_cents, subtotal_cents, discount_cents, discount_code, currency, period_start, period_end, due_at, paid_at, paid_via, voided_at, void_reason, replaces_invoice_id, created_at, updated_at.

Example request

curl "https://app.meloraconnect.com/api/public/v1/dues-invoices?status=paid&updated_since=2026-01-01T00:00:00Z" \
  -H "Authorization: Bearer $MELORA_API_KEY"

Example response, HTTP 200

{
  "data": [
    {
      "id": "c4a1e7b2-6d3f-4e8a-9b1c-2d3e4f5a6b7c",
      "invoice_number": "INV-2026-0142",
      "member_id": "8d2c6f0e-3f59-4a53-9c0e-0f6f5b3a1b21",
      "dues_subscription_id": "f1e2d3c4-b5a6-4978-8a9b-0c1d2e3f4a5b",
      "status": "paid",
      "amount_cents": 25000,
      "subtotal_cents": 25000,
      "discount_cents": 0,
      "discount_code": null,
      "currency": "USD",
      "period_start": "2026-03-02",
      "period_end": "2027-03-01",
      "due_at": "2026-03-02T00:00:00+00:00",
      "paid_at": "2026-02-27T16:40:12+00:00",
      "paid_via": "stripe",
      "voided_at": null,
      "void_reason": null,
      "replaces_invoice_id": null,
      "created_at": "2026-02-01T09:00:00+00:00",
      "updated_at": "2026-02-27T16:40:12+00:00"
    }
  ],
  "page": 1,
  "per_page": 50,
  "has_more": false
}

POST /api/public/v1/webhooks

Subscribe a webhook URL to events. This is what Zapier uses.

Required permission: settings.manage

Parameters

  • url (required): an https address that passes the endpoint checks.
  • events (required): a list of one or more webhook events.
  • include_contact_details (optional): true to send member contact details. The key then also needs members.read.

A new URL answers 201. Subscribing the same URL again replaces its events and answers 200. Each URL counts toward the 10 endpoints an organization can have; past that, the answer is 403 limit_reached.

A URL that staff already added on the Webhooks tab answers 409: remove it there first. If webhooks are not part of the organization's plan, the answer is 403 plan_required.

The answer carries the endpoint's id, url, events and include_contact_details, but no signing secret. The endpoint shows on the Webhooks tab as added through the API, where staff can issue a new secret, shown once, for a receiver that checks signatures.

Example request

curl -X POST "https://app.meloraconnect.com/api/public/v1/webhooks" \
  -H "Authorization: Bearer $MELORA_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"url":"https://hooks.example.com/melora/members","events":["member.created","member.status_changed"]}'

Example response, HTTP 201

{
  "data": {
    "id": "2b3c4d5e-6f7a-4b8c-9d0e-1f2a3b4c5d6e",
    "url": "https://hooks.example.com/melora/members",
    "events": [
      "member.created",
      "member.status_changed"
    ],
    "include_contact_details": false
  }
}

DELETE /api/public/v1/webhooks/{id}

Unsubscribe a webhook that was subscribed through the API.

Required permission: settings.manage

Parameters

  • id (in the path): the id the subscribe call returned.

Answers 204 with no body, and deliveries to that URL stop. An endpoint that staff added on the Webhooks tab, or one of another organization, answers 404: a key never removes an endpoint staff set up.

Example request

curl -X DELETE "https://app.meloraconnect.com/api/public/v1/webhooks/2b3c4d5e-6f7a-4b8c-9d0e-1f2a3b4c5d6e" \
  -H "Authorization: Bearer $MELORA_API_KEY"

Example response, HTTP 204

No body.

Errors

Every error is JSON with a code that does not change, a message for people and sometimes details. Branch on the code, not on the message.

HTTP/1.1 403 Forbidden
Content-Type: application/json; charset=utf-8

{
  "error": {
    "code": "permission_denied",
    "message": "This API key does not have the members.write permission.",
    "details": { "required_permission": "members.write" }
  }
}
HTTP statusError codeMeaning
401invalid_api_keyNo key, a key in the wrong form, or a key Melora Connect does not know. Send the key after Bearer in the Authorization header.
401api_key_revokedThe key was revoked. Make a new one.
401api_key_expiredThe key has passed its expiry date. Make a new one.
403plan_requiredThe plan does not include the API (or the feature the request needs), so the request is refused.
403permission_deniedThe key does not carry the permission this request needs. details.required_permission names it.
403billing_holdThe Melora Connect account is on hold for an unpaid invoice, which pauses this kind of request.
403account_restrictedThe account is restricted, so nothing new can be created.
403limit_reachedA plan limit, for example active members, would be exceeded.
404not_foundNo such record here. Records of other organizations are never shown.
422validation_failedThe request body or a query parameter is not valid. details lists each problem.
409conflictThe record conflicts with one that already exists.
413payload_too_largeThe request body is larger than 64 KB. Send fewer fields or split the work.
429rate_limitedToo many requests. Wait for the number of seconds in the Retry-After header.
500internal_errorSomething failed on Melora Connect's side. Try the request again.

Code examples

Node.js

Node.js 18 or later: read every active member, a page at a time, and wait when the limit is reached.

// Node.js 18 or later: read every active member, a page at a time.
const base = "https://app.meloraconnect.com/api/public/v1";
const headers = { Authorization: `Bearer ${process.env.MELORA_API_KEY}` };

async function allActiveMembers() {
  const members = [];
  for (let page = 1; ; page++) {
    const res = await fetch(`${base}/members?status=active&per_page=100&page=${page}`, { headers });
    if (res.status === 429) {
      await new Promise((r) => setTimeout(r, Number(res.headers.get("retry-after") ?? 60) * 1000));
      page--;
      continue;
    }
    const body = await res.json();
    if (!res.ok) throw new Error(`${body.error.code}: ${body.error.message}`);
    members.push(...body.data);
    if (!body.has_more) return members;
  }
}

Python

Python 3 with requests: add a member, or find the one that already has the email.

# Python 3 with requests: add a member, or find the one that already has the email.
import os, requests

base = "https://app.meloraconnect.com/api/public/v1"
headers = {"Authorization": f"Bearer {os.environ['MELORA_API_KEY']}"}

res = requests.post(f"{base}/members", headers=headers, json={
    "first_name": "Ana", "last_name": "Ruiz", "email": "ana@example.org",
})
if res.status_code == 409:
    member_id = res.json()["error"]["details"]["existing_member_id"]
else:
    res.raise_for_status()
    member_id = res.json()["data"]["id"]

Good practice

  • Give each key only the permissions its system needs.
  • Keep keys on your server. Revoke a key at once if it may have leaked.
  • Every change made with a key appears in the organization's audit log with the key's name.
  • To hear about changes as they happen instead of asking again and again, use webhooks.

See it with your own data

Ready to give your members the platform they deserve?

A walkthrough with our team, built around how your organization works today. Live from 5 weeks after you sign, with a Melora specialist doing the setup.

Book a demoEstimate your go-live date