Skip to content

Membership Plans Query

Note: The Teachify Admin API is currently under development and not yet available for public use. This documentation is provided for preview purposes only.

The Membership Plans API allows you to retrieve information about subscription options available to students in your school. Each plan has specific features, pricing, and duration settings.

Returns a paginated list of membership plans for the authenticated school.

Parameters:

ParameterTypeDescription
filterAdminMembershipPlanFilterFilter criteria (see Filtering)
pageIntPage number for pagination
perPageIntNumber of items per page (default: 20, max: 50)
limitIntAlternative to perPage

Returns:

  • AdminMembershipPlanPage object with:
    • nodes: Array of AdminMembershipPlan objects
    • currentPage: Current page number
    • hasNextPage: Whether there are more pages
    • hasPreviousPage: Whether there are previous pages
    • nodesCount: Total number of items across all pages
    • totalPages: Total number of pages

Example:

query {
membershipPlans(
filter: {
active: true
},
page: 1,
perPage: 10
) {
nodes {
id
name
description
planType
isLifetime
price
currency
interval
intervalCount
active
visible
createdAt
updatedAt
totalRevenue
subscriptions (
filter: {
planId: { eq: "abc123-983dsf8" }
}
) {
nodes {
id
planId
state
createdAt
}
}
}
currentPage
hasNextPage
hasPreviousPage
nodesCount
totalPages
}
}

The AdminMembershipPlan object contains the following fields based on the schema:

FieldTypeDescription
idString!Unique identifier
nameString!Plan name
descriptionStringPlan description
planTypeString!Plan type. Possible values: recurring, fixed_date, specific_length, lifetime
isLifetimeBoolean!Indicates whether this is a lifetime (permanent access) plan
priceFloat!Price of the plan
currencyString!Currency code (e.g., USD, EUR) in ISO 4217 format
intervalString!Billing interval (month, year, day)
intervalCountInt!Number of intervals (e.g., 1, 3, 6)
activeBoolean!Whether the plan is active
visibleBoolean!Whether the plan is visible to students
createdAtInt!When the plan was created
updatedAtInt!When the plan was last updated
soldItemsCountIntNumber of sold subscriptions
totalRevenueFloatTotal revenue generated by this plan
subscriptionsAdminSubscriptionPagePaginated list of subscriptions for this plan
coursesAdminMembershipPlanCoursePagePaginated. Courses this plan grants access to, oldest first. Excludes deleted courses; unpublished ones are listed. See Courses.
metadataJSONCustom external metadata as a JSON object of string key-value pairs. Returns null unless the school has the external_metadata feature enabled. See External Metadata.

courses returns the courses a plan unlocks, so you can map a plan to its catalogue without a second lookup. It is paginated the same way every other nested collection on this API is, and takes the usual page and perPage arguments (default 20, maximum 50). The order is course creation time, oldest first, and courses that have been deleted are left out. A plan that gates only post categories returns an empty page.

Each node is an AdminMembershipPlanCourse — course identity only, no associations:

FieldTypeDescription
idString!Course ID. Pass it to the courses query for the full record.
nameString!Course name
slugString!URL-friendly identifier
courseTypeString!Possible values: paid, public_access, free_redeem, pre_order
invisibleBoolean!Whether the course is hidden from the storefront. Unpublished courses are listed too.

This field is reachable with membership_plans:read alone, which is why it is deliberately narrow. Everything else about a course — its own metadata, lectures, student roster and attachments — comes from the courses query, which requires courses:read. Use the id above to join the two.

query {
membershipPlans {
nodes {
id
name
courses(perPage: 50) {
nodes {
id
name
}
nodesCount
hasNextPage
}
}
}
}

The metadata field returns a JSON object of string key-value pairs that you have configured under your school’s Settings → Advanced → Product Custom Fields. This is the same data delivered with the payment.paid webhook’s lineitems[].metadata payload.

Behavior:

  • Returns null if the school does not have the external_metadata feature enabled.
  • Returns null if no metadata has been set (an empty object is also serialized as null).
  • Otherwise returns the stored hash, e.g. { "erp_product_id": "MP-2024-PRO" }.
  • Filtering by metadata returns no results when the school does not have the feature enabled, matching the null the metadata field reads in that state.

The same field is available on courses, course plans, events and digital downloads. See External Metadata on the Courses page for the full key and value limits.

query {
membershipPlans(filter: { id: { eq: "plan-123" } }) {
nodes {
id
name
metadata # e.g. { "erp_product_id": "MP-2024-PRO" }
}
}
}

The AdminMembershipPlan object includes subscription information through the subscriptions field. This returns a paginated list of subscriptions associated with the plan.

FieldTypeDescription
idString!Unique identifier for the subscription
stateString!Current state of the subscription
startAtIntWhen the subscription starts
endAtIntWhen the subscription ends
currentPeriodStartIntStart of current billing period
currentPeriodEndIntEnd of current billing period
isCancelingBoolean!Whether the subscription is scheduled for cancellation
isCancellableBoolean!Whether the subscription can be cancelled

Each subscription includes user information through the user field:

FieldTypeDescription
idString!User’s unique identifier
nameStringUser’s full name
emailStringUser’s email address
ParameterTypeDescription
idStringOperatorFilter by subscription ID
planIdStringOperatorFilter by subscription plan ID
stateStringOperatorFilter by subscription state

Example with filtered subscriptions:

query {
membershipPlans(page: 1, perPage: 10) {
nodes {
id
name
subscriptions (
filter: {
state: {
eq: "active"
}
}
) {
nodes {
id
state
startAt
endAt
currentPeriodStart
currentPeriodEnd
isCanceling
isCancellable
user {
id
name
email
}
}
currentPage
nodesCount
}
}
}
}

Below is a comprehensive view of fields available in the membershipPlans query based on the schema:

query {
membershipPlans(
filter: {
id: { eq: "plan-123" },
active: true,
visible: true
},
page: 1,
perPage: 20
) {
nodes {
id # Unique identifier (String!)
name # Plan name (String!)
description # Plan description (String)
# Plan type
planType # Plan type: recurring, fixed_date, specific_length, lifetime (String!)
isLifetime # Whether this is a lifetime plan (Boolean!)
# Plan pricing details
price # Price amount (Float!)
currency # Currency code (String!)
# Subscription details
interval # Billing interval (String!)
intervalCount # Number of intervals (Int!)
# Plan status
active # Whether the plan is active (Boolean!)
visible # Whether the plan is visible (Boolean!)
# Timestamps
createdAt # When the plan was created (Int!)
updatedAt # When the plan was last updated (Int!)
# Statistics
soldItemsCount # Number of sold subscriptions (Int)
totalRevenue # Total revenue generated (Float)
# External metadata
metadata # Plan-level external metadata (JSON, nullable)
# Courses this plan unlocks
courses(page: 1, perPage: 50) { # Oldest first, deleted courses excluded (AdminMembershipPlanCoursePage)
nodes {
id
name
slug
courseType
invisible
}
nodesCount
totalPages
hasNextPage
}
# Related subscriptions
subscriptions (
filter: {
planId: { eq: "abc123-983dsf8" }
}
) {
# Subscriptions for this plan (AdminSubscriptionPage)
nodes {
id
state
startAt
endAt
currentPeriodStart
currentPeriodEnd
isCanceling
isCancellable
user {
id
name
email
}
}
currentPage
hasNextPage
nodesCount
}
}
# Pagination information
currentPage # Current page number (Int!)
hasNextPage # Whether there are more pages (Boolean!)
hasPreviousPage # Whether there are previous pages (Boolean!)
nodesCount # Total number of items across all pages (Int!)
totalPages # Total number of pages (Int!)
}
}

The membershipPlans query accepts a filter parameter of type AdminMembershipPlanFilter. This allows you to narrow down results based on various criteria.

Filter FieldTypeDescription
idStringOperatorFilter by plan ID
activeBooleanFilter by active status
visibleBooleanFilter by visibility
planTypeStringOperatorFilter by plan type. Possible values: recurring, fixed_date, specific_length, lifetime
metadata[MetadataFilter!]Filter by external metadata key-value pairs, AND across pairs with exact value matching. Returns no results unless the school has the external_metadata feature enabled. See External Metadata.
updatedAtBigIntOperatorFilter by last update time, given as a Unix timestamp in seconds

eq and neq are exact comparisons against a timestamp stored with microsecond precision: eq matches only a row stored at exactly that whole second, and neq matches every other row. Use gte and lte for incremental sync.

Paging a filtered result is not a change-ordered walk. Pages are ordered by the plan’s display position and then createdAt, neither of which is related to updatedAt, so page boundaries do not follow update order. Reconcile by id rather than assuming a page position corresponds to a point in the change history.

The StringOperator used in filters has these operations:

OperationDescription
eqEqual to
neqNot equal to
inIn a list of values
ninNot in a list of values
likeMatch text values against a pattern using wildcards (case-sensitive)

The BigIntOperator used in timestamp filters has these operations. Unlike StringOperator it has no in or nin. Values must be between 0001-01-01 and 9999-12-31 UTC; a value outside that range is rejected as invalid input:

OperationDescription
eqEqual to
neqNot equal to
gtGreater than
gteGreater than or equal to
ltLess than
lteLess than or equal to

Find active membership plans:

query {
membershipPlans(
filter: {
active: true
}
) {
nodes {
id
name
price
currency
}
nodesCount
}
}

Find visible membership plans:

query {
membershipPlans(
filter: {
visible: true
}
) {
nodes {
id
name
price
currency
}
nodesCount
}
}

Find membership plans by ID:

query {
membershipPlans(
filter: {
id: { eq: "plan-123" }
}
) {
nodes {
id
name
price
currency
}
nodesCount
}
}

Find lifetime membership plans:

query {
membershipPlans(
filter: {
planType: { eq: "lifetime" }
}
) {
nodes {
id
name
planType
isLifetime
price
currency
}
nodesCount
}
}

Find recurring or lifetime plans:

query {
membershipPlans(
filter: {
planType: { in: ["recurring", "lifetime"] }
}
) {
nodes {
id
name
planType
}
nodesCount
}
}

Find all non-recurring plans:

query {
membershipPlans(
filter: {
planType: { neq: "recurring" }
}
) {
nodes {
id
name
planType
}
nodesCount
}
}

Find a membership plan by its external part number:

query {
membershipPlans(
filter: {
metadata: [{ key: "erp_product_id", value: "MP-2024-PRO" }]
}
) {
nodes {
id
name
planType
metadata
}
nodesCount
}
}