# Joining

> Implement the membership purchase flow end to end, from pricing to a paid-up member.

HTML version: [Joining](https://support.gladstonesoftware.cloud/guides/joining)

The recommended API flow for new member joining.

The joining flow uses tags to filter the membership options to those relevant to the joining member. We recommend structuring your tag selection by Member Type > Membership Duration > Membership Types. Once you have taken the member through this flow, they can select a membership, review the pricing, enter their details, and pay.

This flow is for new members buying memberships for the first time. For an existing member buying a new membership, you can use the [Create a Membership](../reference/joining/create-a-membership-for-a-member.md) endpoint.

1. [GET - api/configuration/tag-groups/\{MEMBER\_TYPES}](../reference/configuration/get-all-tag-groups-with-optional-type-filtering.md)

This endpoint returns the details of the member type tags that have been set up in your account, e.g. Senior, Adult, to display to the joining member.

**Request**

```curl
curl --request GET \
     --url 'https://example.com/api/configuration/tag-groups?type=%7BMEMBER_TYPES%7D' \
     --header 'accept: application/json
```

**Response**

```json
[
  {
    "id": 0,
    "key": "string",
    "name": "string",
    "description": "string",
    "type": "string",
    "allowMultipleTags": true,
    "sortOrder": 0,
    "tags": [
      {
        "id": 0,
        "key": "string",
        "name": "string",
        "description": "string",
        "sortOrder": 0,
        "isSecondary": true,
        "translations": [
          {
            "culture": "string",
            "type": "string",
            "text": "string"
          }
        ]
      }
    ],
    "translations": [
      {
        "culture": "string",
        "type": "string",
        "text": "string"
      }
    ]
  }
]
```

The Tag Key or ID for the selected tag is passed into the next call as a filter.

2. [GET - api/configuration/tag-groups/\{MEMBERSHIP\_DURATIONS}?filters=\{ADULT}](../reference/configuration/get-tag-group-by-id-or-key-with-optional-filtering.md)

This endpoint returns the details of the membership duration tags, filtered by the previous selected tag. This ensures the joining member is only displayed relevant options to choose from that will display a membership type at the end.

In this example request we have filtered by the Adult member type tag.

**Request**

```curl
curl --request GET \
     --url 'https://example.com/api/configuration/tag-groups/MEMBERSHIP_DURATIONS?selected-tags=ADULT' \
     --header 'accept: application/json'
```

**Response**

```json
{
  "id": 0,
  "key": "string",
  "name": "string",
  "description": "string",
  "type": "string",
  "allowMultipleTags": true,
  "sortOrder": 0,
  "tags": [
    {
      "id": 0,
      "key": "string",
      "name": "string",
      "description": "string",
      "sortOrder": 0,
      "isSecondary": true,
      "translations": [
        {
          "culture": "string",
          "type": "string",
          "text": "string"
        }
      ]
    }
  ],
  "translations": [
    {
      "culture": "string",
      "type": "string",
      "text": "string"
    }
  ]
}
```

3. [GET - api/configuration/tag-groups/\{MEMBERSHIP\_TYPES}](../reference/configuration/get-all-tag-groups-with-optional-type-filtering.md)

This endpoint returns the different types of membership. The default options are individual or group. You can either have the member select an option, or display both using the next call. It's not necessary to filter this call by the previous tags as all memberships should have only one of the two Membership Types.

**Request**

```curl
curl --request GET \
     --url 'https://example.com/api/configuration/tag-groups?type=%7BMEMBERSHIP_TYPES%7D' \
     --header 'accept: application/json'
```

**Response**

```json
[
  {
    "id": 0,
    "key": "string",
    "name": "string",
    "description": "string",
    "type": "string",
    "allowMultipleTags": true,
    "sortOrder": 0,
    "tags": [
      {
        "id": 0,
        "key": "string",
        "name": "string",
        "description": "string",
        "sortOrder": 0,
        "isSecondary": true,
        "translations": [
          {
            "culture": "string",
            "type": "string",
            "text": "string"
          }
        ]
      }
    ],
    "translations": [
      {
        "culture": "string",
        "type": "string",
        "text": "string"
      }
    ]
  }
]
```

4. [POST - api/memberships/membership-types-by-tag-group](../reference/joining/get-membership-types-grouped-by-tag.md)

Use the selected tags in this call as filters along with the Site ID, and tagGroupKey of MembershipFacilities. We also recommend including the Headline Price and filtering by Web Bookable.

**Request**

```curl
curl --request POST \
     --url https://example.com/api/memberships/membership-types-by-tag-group \
     --header 'accept: application/json' \
     --header 'content-type: application/*+json' \
     --data '
{
  "includeHeadlinePrice": true,
  "tagKeys": [
    "ADULT",
    "DD",
    "INDIVIDUAL"
  ],
  "tagGroupKey": "MEMBERSHIP_FACILITIES",
  "siteId": "WIN",
  "isWebBookable": true
}
'
```

**Response**

```json JSON
[
  {
    "groupTag": {
      "id": 0,
      "key": "string",
      "name": "string",
      "description": "string",
      "sortOrder": 0,
      "isSecondary": true,
      "translations": [
        {
          "type": "string",
          "culture": "string",
          "text": "string"
        }
      ]
    },
    "membershipTypes": [
      {
        "id": "string",
        "description": "string",
        "applyToLinks": true,
        "minAge": 0,
        "maxAge": 0,
        "imageUrl": "string",
        "priceLevelId": "string",
        "tagGroups": [
          {
            "id": 0,
            "key": "string",
            "name": "string",
            "description": "string",
            "type": "string",
            "allowMultipleTags": true,
            "sortOrder": 0,
            "tags": [
              {
                "id": 0,
                "key": "string",
                "name": "string",
                "description": "string",
                "sortOrder": 0,
                "isSecondary": true,
                "translations": [
                  {
                    "type": "string",
                    "culture": "string",
                    "text": "string"
                  }
                ]
              }
            ],
            "translations": [
              {
                "type": "string",
                "culture": "string",
                "text": "string"
              }
            ]
          }
        ],
        "translations": [
          {
            "type": "string",
            "culture": "string",
            "text": "string"
          }
        ]
      }
    ]
  }
]
```

This will display membership types that are available for the selected tags, display any options for facility level, e.g. Gym only, Class only, Premium, display the price of the membership before any discounts or pro-rata, and filter by those that can be purchased online only.

5. [POST - api/pricing/memberships](https://gladstone-api.readme.io/reference/post_api-pricing-memberships)

Use the selected membership ID from the previous step in this endpoint to receive a pricing quote with a timestamp and a signature.

**Request**

```curl
curl --request POST \
     --url https://example.com/api/pricing/memberships \
     --header 'accept: application/json' \
     --header 'content-type: application/*+json' \
     --data '
{
  "siteId": "WIN",
  "members": [
    {
      "memberships": [
        {
          "id": "ASUBDD0001"
        }
      ],
      "orderKey": 1,
      "memberId": 0
    }
  ],
  "startDate": "2024-10-18T00:00:00Z"
}
'
```

**Response**

```json
{
  "siteId": "string",
  "promotionCode": "string",
  "overview": {
    "now": {
      "amount": "string",
      "currency": "string",
      "discount": {
        "priceBeforeDiscount": "string",
        "discountValue": "string",
        "discountId": 0
      }
    },
    "recurring": {
      "headlinePrice": {
        "amount": "string",
        "currency": "string",
        "discount": {
          "priceBeforeDiscount": "string",
          "discountValue": "string",
          "discountId": 0
        }
      },
      "frequency": "string",
      "schedule": [
        {
          "date": "2024-10-18T06:33:37.011Z",
          "totalPrice": {
            "amount": "string",
            "currency": "string",
            "discount": {
              "priceBeforeDiscount": "string",
              "discountValue": "string",
              "discountId": 0
            }
          },
          "breakdown": [
            {
              "price": {
                "amount": "string",
                "currency": "string",
                "discount": {
                  "priceBeforeDiscount": "string",
                  "discountValue": "string",
                  "discountId": 0
                }
              },
              "paymentMethod": {
                "id": "string",
                "description": "string",
                "translations": [
                  {
                    "type": "string",
                    "culture": "string",
                    "text": "string"
                  }
                ]
              }
            }
          ]
        }
      ]
    }
  },
  "memberships": [
    {
      "orderKey": 0,
      "id": "string",
      "description": "string",
			"coolingOffPeriod": 0,
      "translations": [
        {
          "type": "string",
          "culture": "string",
          "text": "string"
        }
      ],
      "startDate": "2024-10-18T06:33:37.011Z",
      "endDate": "2024-10-18T06:33:37.011Z",
      "pricing": {
        "now": {
          "totalPrice": {
            "amount": "string",
            "currency": "string",
            "discount": {
              "priceBeforeDiscount": "string",
              "discountValue": "string",
              "discountId": 0
            }
          },
          "breakdown": [
            {
              "priceLevelId": "string",
              "productId": "string",
              "type": "string",
              "description": "string",
              "translations": [
                {
                  "type": "string",
                  "culture": "string",
                  "text": "string"
                }
              ],
              "price": {
                "amount": "string",
                "currency": "string",
                "discount": {
                  "priceBeforeDiscount": "string",
                  "discountValue": "string",
                  "discountId": 0
                }
              }
            }
          ]
        },
        "recurring": {
          "membershipId": 0,
          "headlinePrice": {
            "amount": "string",
            "currency": "string",
            "discount": {
              "priceBeforeDiscount": "string",
              "discountValue": "string",
              "discountId": 0
            }
          },
          "frequency": "string",
          "schedule": [
            {
              "date": "2024-10-18T06:33:37.011Z",
              "payments": [
                {
                  "price": {
                    "amount": "string",
                    "currency": "string",
                    "discount": {
                      "priceBeforeDiscount": "string",
                      "discountValue": "string",
                      "discountId": 0
                    }
                  },
                  "paymentMethod": {
                    "id": "string",
                    "description": "string",
                    "translations": [
                      {
                        "type": "string",
                        "culture": "string",
                        "text": "string"
                      }
                    ]
                  }
                }
              ]
            }
          ]
        }
      }
    }
  ],
  "timeStamp": "string",
  "signature": "string"
}
```

This entire response object is required for the next steps.

6. [POST - api/memberships/add-members-with-memberships](../reference/joining/create-members-with-memberships.md)

To send this endpoint, you need joining members to enter their details which are used in the request object along with the pricing quote from the previous step. The member ID and sales invoice ID are returned in the response.

You can add multiple members at the same time by including multiple member objects. These should have unique [Order Keys](glossary.md).

**Request**

```curl
curl --request POST \
     --url https://example.com/api/memberships/add-members-with-memberships \
     --header 'accept: application/json' \
     --header 'content-type: application/*+json' \
     --data '
{
  "primaryMember": {
    "profile": {
      "titleId": "MS",
      "firstName": "Carole",
      "lastName": "Smith",
      "email": "carole.smith@email.email",
      "dateOfBirth": "2001-10-18T00:00:00Z",
      "siteId": "WIN",
      "memberLanguage": "en-GB"
    },
    "addressDetails": {
      "address1": {
        "addressLine1": "Hithercroft Rd",
        "townCity": "Wallingford",
        "postalCode": "OX10 9BT",
        "country": "United Kingdom",
        "isPrimary": true
      }
    },
    "contactDetails": {
      "mobile": {
        "number": "+447123456789",
        "isPrimary": true
      }
    },
    "bankDetails": {
      "accountName": "Account",
      "accountNumber": "12345678",
      "sortCode": "123456"
    },
    "signedAgreements": {
      "privacyPolicy": true,
      "termsAndConditions": true,
      "healthCommitmentStatement": true
    },
    "optInMarketing": true
  },
  "pricingQuote": {
    "overview": {
      "now": {
        "amount": "46.51",
        "currency": "GBP"
      },
      "recurring": {
        "headlinePrice": {
          "amount": "25.00",
          "currency": "GBP"
        },
        "frequency": "Monthly",
        "schedule": [
          {
            "totalPrice": {
              "amount": "25.00",
              "currency": "GBP"
            },
            "date": "2024-12-01T00:00:00"
          },
          {
            "totalPrice": {
              "amount": "25.00",
              "currency": "GBP"
            },
            "date": "2025-01-01T00:00:00"
          },
          {
            "totalPrice": {
              "amount": "25.00",
              "currency": "GBP"
            },
            "date": "2025-02-01T00:00:00"
          }
        ]
      }
    },
    "siteId": "WIN",
    "memberships": [
      {
        "pricing": {
          "now": {
            "totalPrice": {
              "amount": "46.51",
              "currency": "GBP"
            },
            "breakdown": [
              {
                "price": {
                  "amount": "10.00",
                  "currency": "GBP"
                },
                "priceLevelId": "ADULT",
                "productId": "AADMIN0001",
                "type": "one-off",
                "description": "Admin / Joining Fee"
              },
              {
                "price": {
                  "amount": "36.51",
                  "currency": "GBP"
                },
                "priceLevelId": "ADULT",
                "productId": "ASUBDD0001",
                "type": "pro-rata",
                "description": "Premium Adult D D"
              }
            ]
          }
        },
        "orderKey": 1,
        "id": "ASUBDD0001",
        "description": "Premium Adult D D",
        "endDate": "2024-11-30T00:00:00",
        "startDate": "2024-10-18T00:00:00Z"
      }
    ],
    "timeStamp": "2024-10-18T13:10:34.0005164Z",
    "signature": "abcdefghijklmnopQRSTUVWXZY123456789=="
  },
  "siteId": "WIN",
  "hasGuardian": false,
  "memberLanguage": "en-GB"
}
'
```

**Response**

```json
{
  "membersCreated": [
    {
      "orderKey": 0,
      "memberId": 0,
      "bankAccountReference": "string",
      "salesInvoiceIds": [
        0
      ]
    }
  ],
  "membershipsCreated": [
    {
      "memberId": 0,
      "membershipIsDirectDebit": true,
      "membershipId": 0,
      "salesInvoiceIds": [
        0
      ]
    }
  ],
  "timeStamp": "string",
  "signature": "string"
}
```

The endpoint creates the members and assigns their memberships. The next stage is to use the hosted forms to create a payment token. The details of how to do this can be found in the [Hosted Form Integration Guide](https://guides.gitbook.io/integrationguide/references/code-references/integration-libraries/hosted-payment-fields-library) from our payment partner.

Once you have the payment token, then move through the payment endpoint.

7. [POST - api/payment/pay](../reference/payments/pays-for-sales-invoices-using-a-specified-payment-provider-and-can-handle-paymen.md)

Use the member ID and sales invoice ID returned from the previous step, then add in details of the payment method to then send the request.

This endpoint will return an initial response stating that 3DS challenge is required. At this point we take the 3DS challenge details, append it to our request and use the payment/pay endpoint again with the refreshed details.

For RCP memberships, both the saveCardToWallet and saveRCPcard must be set to True. This saves the card against the member's profile in the system.

**Request**

```curl
curl --request POST \
     --url https://example.com/api/payment/pay \
     --header 'accept: application/json' \
     --header 'content-type: application/*+json' \
     --data '
{
  "deviceInformation": {
    "timeZone": "-60",
    "capabilities": "java, javascript",
    "screenResolution": "1200x1920x24"
  },
  "salesInvoiceIds": [
    12345678
  ],
  "memberId": 87654321,
  "paymentToken": "987654321qwretyuipasdfghjklZXCVBNM",
  "saveCardToWallet": false,
  "saveRCPCard": false
}
'
```

**Response**

```json
{
  "responseStatus": "InternalServerError",
  "transactionId": "string",
  "walletId": "string",
  "cardId": "string",
  "responseCode": 0,
  "paymentProviderReference": "string",
  "threeDSecureChallengeUrl": "string",
  "threeDSecureChallengeFields": {
    "additionalProp": "string"
  }
}
```
