API adding members subscriptions

The WisePub API lets your CRM, checkout or automation tool add members, subscribe them to plans, cancel subscriptions and keep member details in sync. This article covers every endpoint available with your site’s API key, the parameters each one accepts, and the responses you can expect.

All endpoints are relative to your site’s address, for example https://yoursite.com/api/subscription/12.

Authentication

The API uses an API key to authenticate access. You may insert the provided API key in your request’s Authorization header like the following:

Authorization: Bearer {API key}

The other option is to add the API key in the key parameter for every request. For the read (GET) endpoints, add it to the query string, for example ?key={API key}.

If the key is missing or does not match, the request is rejected with a 403 response.

Request format: POST endpoints accept form data or JSON. When sending JSON, set the Content-Type: application/json header. True/false parameters accept true, false, 1, 0, yes or no.

Subscribers

New Subscriber

POST /api/subscription/{Subscription ID}

Insert the ID of the subscription you want the user to subscribe to. If the user does not exist, we create it. If the user exists (matched by email), we update it.

Query Parameters

Parameters Description
key API key provided. Required if not set in request Authorization header.
email Required. Email of the subscriber. Must include a valid domain extension (for example .com).
external_id Optional. External ID of the subscriber, such as the contact ID in your CRM. Can be any text or number.
contact_id Optional. Alternative name for external_id. If both are sent, contact_id is used.
source String identifier of source, such as the name of the system sending the request. Default to ‘api’ if not set when the member is created.
first_name First name of the subscriber. Defaults to “Student” for new members.
last_name Last name of the subscriber. Defaults to “Member” for new members.
password Password of the subscriber. (if empty, a random one will be generated). Only used when the member is created.
phone_number Phone number of the user format should be → 1234567890
live_scanner True or false (this is only if you use scanner with F2-Tech)
welcome_email True or false. Determines if the welcome email should be sent. Default is true.

Example:

curl -X POST "https://yoursite.com/api/subscription/12" \
  -H "Authorization: Bearer {API key}" \
  -d "[email protected]" \
  -d "first_name=Jane" \
  -d "last_name=Doe" \
  -d "external_id=A-10045" \
  -d "source=my-crm"

What happens to existing members

  • Not subscribed, or subscription expired or inactive: the member is subscribed again and receives the welcome email (unless it is turned off).
  • Already has an active subscription to this plan: the subscription is left as is and no welcome email is sent. Their other details (name, phone number, etc.) are still updated.
  • Existing members keep their current password. Their welcome email asks them to continue using it.

About the welcome email

The welcome email is sent only when all of the following are true:

  • welcome_email is not set to false
  • The Disable API welcome email setting is turned off (Settings > Members & Subscriptions)
  • The subscription itself does not have its welcome email disabled

Webhooks with customData (GoHighLevel)

If your request body contains a customData object, as GoHighLevel webhooks do, each key inside it is read as if it were a regular parameter. For example, customData.email is used as email.

If the request includes a location object with an id (also sent by GoHighLevel), that location ID is saved as the member’s source.

Subscribe Member To Multiple Subscriptions at once

POST /api/member/subscribe

This endpoint creates or updates a member and subscribes them to one or more subscription plans.

Parameters Description
key API key provided. Required if not set in request Authorization header.
email Required. Email of the subscriber. Must include a valid domain extension.
external_id Required. External ID of the subscriber. Can be any text or number.
source String identifier of source. Default to ‘api’ if not set when the member is created.
first_name First name of the subscriber.
last_name Last name of the subscriber.
password Password of the subscriber. (if empty, a random one will be generated). Only used when the member is created.
phone_number Phone number of the user format should be → 1234567890
live_scanner True or false (this is only if you use scanner with F2-Tech)
welcome_email True or false. Determines if welcome email should be sent. Default is true.
subs_ids Array of subscription IDs to subscribe the user to. Every ID must belong to an existing subscription.

Please understand the subs_ids need to be an array. In a system like Infusionsoft you might need to input it like this:

subs_ids[0] = 1
subs_ids[1] = 2
subs_ids[2] = 3

In JSON, send it as a list: "subs_ids": [1, 2, 3].

Pro Tip: When updating an existing member, always send first_name and last_name. If they are left out, this endpoint resets them to “Student” and “Member”.

If the external_id and source you send already belong to a different member, the request returns a 409 response. See Responses and errors.

Cancel Subscriptions

POST /api/subscription/delete/{Subscription ID}

Insert the ID of the subscription you want the user to unsubscribe from.

Parameters Description
key API key provided. Required if not set in request Authorization header.
email Required. Email of the user to unsubscribe.

The member’s account is kept. Only their access to this subscription is removed.

Members

Update member data

POST /api/member/update/{external ID}

Use this endpoint to update a member you previously added with an external ID.

The member is identified by the external_id and source sent in the request body, not by the external ID in the address. Always send external_id in the body. Keep the external ID in the address as well so existing integrations continue to work.

All other parameters are optional. For example, you can add only the email if this is the only data that you wish to update.

Parameters Description
key API key provided. Required if not set in request Authorization header.
external_id Required. External ID of the user.
source String identifier of source. Must match the source the member was created with. Defaults to ‘api’ if not set.
contact_id Optional. If sent, replaces the member’s external ID with this value.
email New user email.
first_name New first name of the user.
last_name New last name of the user.
password New password of the user.
phone_number Phone number of the user format should be → 1234567890
live_scanner True or false

Pro Tip: If you get a “User not found” response, check the source. Members created with a custom source (or a GoHighLevel location ID) can only be found when that same source is sent. Leaving it out searches for members with the source “api”.

A few things to know:

  • Only users with the Member role can be updated through this endpoint. Staff accounts return “User not found”.
  • If the request includes a location object with an id, it is saved as the member’s new source.
  • This endpoint updates member details only. It does not change subscriptions.

Update member by email

POST /api/member/update-by-email

Use this endpoint when you know the member’s email address but not their external ID. It is useful for adding an external ID to members who were created without one.

Parameters Description
key API key provided. Required if not set in request Authorization header.
email Required. Email of the member to update.
first_name New first name of the user.
last_name New last name of the user.
external_id New external ID of the user.
contact_id Optional. Alternative name for external_id. If both are sent, contact_id is used.
source New string identifier of source.
password New password of the user.
phone_number Phone number of the user format should be → 1234567890
live_scanner True or false

A few things to know:

  • The email is used to find the member, so it cannot be changed here. To change a member’s email, use Update member data above.
  • Only users with the Member role can be updated. Other users return “User not found”.
  • If the request includes a location object with an id, it is saved as the member’s source.
  • This endpoint updates member details only. It does not change subscriptions.

Example:

curl -X POST "https://yoursite.com/api/member/update-by-email" \
  -H "Authorization: Bearer {API key}" \
  -d "[email protected]" \
  -d "external_id=A-10045" \
  -d "source=my-crm"

Read endpoints

These endpoints return information from your site. They use the same API key and are called with GET.

Member count

GET /api/user-count

Returns the total number of user accounts on your site, including staff accounts.

{
  "data": {
    "count": 1250
  }
}

List subscriptions

GET /api/subscriptions

Returns every subscription on your site. Use it to look up the subscription IDs needed by the endpoints above.

{
  "subscriptions": [
    {
      "id": 12,
      "name": "Premium",
      "public_name": "Premium Membership",
      "slug": "premium",
      "description": "Full access to all rooms",
      "order": 1,
      "is_public": true,
      "redirect_url": null,
      "restricted_access_text": null,
      "mobile_push_enabled": false
    }
  ]
}

Each subscription also includes a few display settings, such as its theme and logo.

Portfolio

GET /api/portfolios/{Portfolio ID}

Returns one portfolio with its trades. This endpoint is only available when the Portfolio feature is enabled on your site.

Parameters Description
key API key provided. Required if not set in request Authorization header.
{
  "success": true,
  "portfolio": { "id": 3, "name": "Swing Trades", "slug": "swing-trades", "description": "..." },
  "market_data": true,
  "fields": [ { "id": 1, "name": "Setup", "type": "text" } ],
  "openTrades": [
    {
      "id": 41,
      "symbol": "AAPL",
      "entry_date": "2026-09-30T00:00:00.000000Z",
      "entry_price": "182.10",
      "is_short": false,
      "last_value": 185.4,
      "pc_change": "0.85",
      "pc_change_entry": "1.81",
      "fields": { "Setup": "Breakout" }
    }
  ],
  "closedTrades": [
    {
      "id": 38,
      "symbol": "MSFT",
      "entry_date": "2026-08-12T00:00:00.000000Z",
      "exit_date": "2026-09-02T00:00:00.000000Z",
      "entry_price": "410.00",
      "exit_price": "428.50",
      "pc_change": "4.51",
      "time_difference": 21,
      "fields": { "Setup": "Pullback" }
    }
  ]
}
  • fields lists the portfolio’s custom columns. Each trade’s fields object holds its value for each column, keyed by column name.
  • last_value, pc_change and pc_change_entry on open trades come from live market data and are empty when it is not available.
  • On closed trades, pc_change is the gain or loss in percent and time_difference is the number of days the trade was held.

Calendar events

GET /api/calendar/events

Returns every calendar event on your site, including events that are not linked to a subscription. Results are paginated.

Parameters Description
key API key provided. Required if not set in request Authorization header.
updated_since Optional. Date and time (for example 2026-10-01T00:00:00Z). Only returns events changed since then.
per_page Optional. Number of events per page, from 1 to 500. Default is 100.
cursor Optional. The next_cursor value from the previous page, to get the next page.
{
  "data": [
    {
      "id": 88,
      "name": "Morning Market Briefing",
      "description": "Daily pre-market review",
      "url": null,
      "color": "#2563eb",
      "start_at": "2026-10-20T13:30:00+00:00",
      "end_at": "2026-10-20T14:30:00+00:00",
      "recurring": true,
      "recurring_rule": "FREQ=WEEKLY;BYDAY=MO,TU,WE,TH,FR",
      "recurrence_end_at": null,
      "suppressed_dates": [],
      "track_attendees": false,
      "parent_event_id": null,
      "original_start_at": null,
      "room": { "id": 5, "name": "Main Room", "slug": "main-room" },
      "calendar": { "id": 1, "name": "Live Events" },
      "subscriptions": [ { "id": 12, "name": "Premium", "slug": "premium", "sku": "PREM" } ]
    }
  ],
  "meta": { "next_cursor": "eyJpZCI6ODgsIl9wb2ludHNUb05leHRJdGVtcyI6dHJ1ZX0", "per_page": 100 },
  "app_name": "Your Site",
  "app_url": "https://yoursite.com"
}

When next_cursor is null, you have reached the last page. Deleted events are not returned.

Available chat rooms

GET /api/rooms/available

Returns every room with chat turned on that is not hidden, along with the subscriptions that give access to it. Results are paginated.

Parameters Description
key API key provided. Required if not set in request Authorization header.
per_page Optional. Number of rooms per page, from 1 to 500. Default is 100.
cursor Optional. The next_cursor value from the previous page, to get the next page.
{
  "data": [
    {
      "id": 5,
      "name": "Main Room",
      "slug": "main-room",
      "description": "Live trading room",
      "chat_paused": false,
      "moderated": true,
      "cash_tags": true,
      "private_mode": false,
      "allow_anonymous": false,
      "subscriptions": [ { "id": 12 }, { "id": 14 } ]
    }
  ],
  "meta": { "next_cursor": null, "per_page": 100 },
  "app_name": "Your Site",
  "app_url": "https://yoursite.com"
}

A room is open to all members when its subscriptions list is empty or allow_anonymous is true. Otherwise, a member needs an active subscription to at least one of the listed subscriptions. Members open a room at https://yoursite.com/room/{slug}.

Responses and errors

Every response is JSON. Use the status code to know whether the request worked.

Status Meaning
200 Success. POST endpoints return a message: “User created successfully”, “User updated successfully”, “User subscribed successfully” or “User unsubscribed successfully”.
403 The API key is missing or incorrect. The response body is empty.
404 “User not found”: no member matches the email, or the external ID and source you sent. An empty 404 means the subscription or portfolio ID in the address does not exist.
409 “Duplicate external ID conflict”: see below.
422 Validation error. A required parameter is missing or a value is not valid. The response lists each problem.

Success example:

{ "message": "User created successfully" }

Validation error example (422):

{
  "message": "The email field must be a valid email address.",
  "errors": {
    "email": ["The email field must be a valid email address."]
  }
}

Duplicate external ID conflict (409)

Each combination of source and external_id can belong to only one member. A 409 response means the request would give a member a source and external ID that another member already has. It usually happens when two contacts in your CRM share an ID, or when a contact’s email changed in your CRM and the old WisePub account still holds the ID.

What happens next depends on the Match API updates on source and external ID setting, found in Settings > Members & Subscriptions under API and access:

  • On (default): updates to an existing member are rejected with a 409, and nothing is changed.
  • Off: updates to an existing member (New Subscriber, Subscribe Member and Update member by email) go through. The member keeps their current source and external ID, and every other change is saved.

Creating a new member with a source and external ID that are already taken, and the Update member data endpoint, always return a 409.

Pro Tip: If you set an Error report email in Settings > Site, WisePub sends a notification to that address each time a duplicate external ID conflict occurs, with the email, source and external ID involved.

Tips & Best Practices

  • Always send the same source for a given system. It is how WisePub tells members from different systems apart.
  • Use GET /api/subscriptions to confirm subscription IDs before setting up your integration.
  • Set welcome_email to false when importing existing customers who already have access, so they are not emailed again.
  • Keep your API key private. Anyone with the key can add, update and unsubscribe members.