Step 8 – Set Up Payment Plans
Configure payment plans (create, update, bind, delete)
Create a payment plan
-
API: createPaymentPlan
-
Purpose & Use Cases: Use this API to configure optional or mandatory non-rental charges that should be collected from guests during the booking or payment process. Examples include:
- Admin Fee
- Booking Fee
- Utility Surcharge
- One-time Processing Fee These charges can be added to the payment plan and displayed clearly to guests before confirming their booking.
- Validation Rules:
- If landlord Payment Access is set to
ALLOWED, the field paymentOnline must be provided. - If landlord Payment Access is set to
DENIED, the field paymentOnline must not be set. - The value of paymentCurrency must be the ISO currency code of the country where the property is located. All properties bound to the payment plan must use the same currency.
- If category is set to
DEPOSIT_IS_REQUIRED, the apCategory of the bound properties must be set toKEY.
- If landlord Payment Access is set to
- Request:
| Attribute Name | Data Type | Required | Details |
|---|---|---|---|
| paymentPlanName | String | Yes | The name of the payment plan |
| paymentCurrency | CurrencyUnit | Yes | Currency code used for payments (e.g., USD, GBP) |
| description | String | No | Short description of the payment plan |
| effectiveFromDate | Date | Yes | Start date of the payment plan (format: YYYY-MM-DD) |
| effectiveEndDate | Date | Yes | End date of the payment plan (format: YYYY-MM-DD) |
| appliedProperties | CreatePaymentPlanProperty[] | No | List of property IDs the plan applies to; CreatePaymentPlanProperty is an object includes, propertyId: ID! |
| paymentOnline | Boolean | No | If students must pay online. Set to true if required |
| category | PaymentPlanCategory | No | Category of the payment plan; Possible values: DEPOSIT_IS_REQUIRED RENT_IS_REQUIREDDefault value: RENT_IS_REQUIRED |
QUERY
mutation CreatePaymentPlan($input: CreatePaymentPlanInput) {
createPaymentPlan(input: $input) {
paymentPlan {
id
landlordId
name
currency
description
startDate
endDate
createdAt
updatedAt
properties {
id
name
slug
status
bookingJourney
}
deposits {
id
paymentPlanId
name
paymentType
amount
price
currency
description
createdAt
updatedAt
}
fees {
id
paymentPlanId
name
paymentType
amount
price
currency
description
createdAt
updatedAt
}
instalments {
id
paymentPlanId
name
fromTenancy
toTenancy
tenancyUnit
fullPayment
payNowAmount
payWeekly
payRemainderAtOnce
payQuarterly
payMonthly
requireGuarantor
country {
id
name
countryCode
currency
rentCycle
}
updatedAt
createdAt
}
active
paymentOnline
category
}
}
}
GRAPHQL VARIABLES
{
"input": {
"category": "RENT_IS_REQUIRED",
"paymentCurrency": "GBP",
"description": "Created from open API",
"paymentPlanName": "open api create payment plan Test",
"effectiveFromDate": "2025-05-20",
"effectiveEndDate": "9999-12-31",
"paymentOnline":false
}
}
- Response:
{
"data": {
"createPaymentPlan": {
"paymentPlan": {
"id": "eyJ0eXBlIjoiUGF5bWVudFBsYW4iLCJpZCI6MTMwfQ==",
"landlordId": "eyJ0eXBlIjoiTGFuZGxvcmQiLCJpZCI6NDEyN30=",
"name": "open api create payment plan Test",
"currency": "GBP",
"description": "Created from open API",
"startDate": "2025-05-29",
"endDate": "9999-12-31",
"createdAt": "2025-05-29T07:38:37+00:00",
"updatedAt": "2025-05-29T07:38:37+00:00",
"properties": [],
"deposits": [],
"fees": [],
"instalments": [],
"active": false,
"paymentOnline": false,
"category": "RENT_IS_REQUIRED"
}
}
}
}
- Error Message:
| Error Message | Description |
|---|---|
| Landlord`s No Payment Access is allowed, paymentOnline must be true or false | Unable to create a payment plan because landlord.allowNoPayment is true and input.paymentOnline is empty |
| When paymentOnline is false, category must be rent_is_required | Unable to create a payment plan because paymentOnline = false and the category is not RENT_IS_REQUIRED |
| NVALID_EFFECTIVE_FROM_DATE_FORMAT | Unable to proceed because input.effectiveFromDate is missing or incorrectly formatted |
| INVALID_EFFECTIVE_END_DATE_FORMAT | Unable to proceed because input.effectiveEndDate is missing or incorrectly formatted |
| INVALID_EFFECTIVE_FROM_DATE | Unable to proceed because effectiveFromDate < NOW |
| INVALID_EFFECTIVE_END_DATE | Unable to proceed because effectiveFromDate > effectiveEndDate |
| INVALID_LANDLORD_PROPERTY | Unable to proceed because the provided propertyIds contain duplicates or invalid entries |
| INVALID_PROPERTY_BOOKING_JOURNEY | Unable to proceed because property.bookingJourney is not in [BOOK, CONNECT] |
| INVALID_PAYMENT_CURRENCY | Unable to proceed because property.currency !== paymentCurrency |
| INVALID_PROPERTY | Unable to proceed because the input contains properties with more than one currency |
Update Payment Plan
-
API: updatePaymentPlan
-
Purpose & Use Cases: This API allows you to update the basic information of the payment plan
-
Request:
| Attribute Name | Data Type | Required | Details |
|---|---|---|---|
| id | ID | Yes | The unique ID of the newly created payment plan |
| paymentPlanName | String | No | The name of the payment plan |
| description | String | No | Short description of the payment plan |
| effectiveFromDate | Date | No | Start date of the payment plan (format: YYYY-MM-DD) |
| effectiveEndDate | Date | No | End date of the payment plan (format: YYYY-MM-DD) |
QUERY
mutation UpdatePaymentPlan($input: UpdatePaymentPlanInput) {
updatePaymentPlan(input: $input) {
paymentPlan {
id
name
paymentOnline
active
category
createdAt
currency
deposits {
id
paymentPlanId
name
paymentType
amount
price
currency
description
createdAt
updatedAt
}
description
endDate
fees {
id
paymentPlanId
name
paymentType
amount
price
currency
description
createdAt
updatedAt
}
instalments {
id
paymentPlanId
name
fromTenancy
toTenancy
tenancyUnit
fullPayment
payNowAmount
payWeekly
payRemainderAtOnce
payQuarterly
payMonthly
requireGuarantor
updatedAt
createdAt
}
landlordId
startDate
updatedAt
}
}
}
GRAPHQL VARIABLES
{
"input": {
"id": "eyJ0eXBlIjoiUGF5bWVudFBsYW4iLCJpZCI6MzY5fQ",
"paymentPlanName": "open api create payment plan Test",
"description": "just for test"
}
}
- Response:
{
"data": {
"updatePaymentPlan": {
"paymentPlan": {
"id": "eyJ0eXBlIjoiUGF5bWVudFBsYW4iLCJpZCI6MTI1fQ==",
"name": "open api create payment plan Test",
"paymentOnline": false,
"active": null,
"category": "RENT_IS_REQUIRED",
"createdAt": "2025-05-20T06:45:16+00:00",
"currency": "GBP",
"deposits": null,
"description": "just for test",
"endDate": "9999-12-31",
"fees": null,
"instalments": null,
"landlordId": "eyJ0eXBlIjoiTGFuZGxvcmQiLCJpZCI6NDEyN30=",
"startDate": "2025-05-20",
"updatedAt": "2025-05-29T07:48:34+00:00"
}
}
}
}
- Error Message:
| Error Message | Description |
|---|---|
| published property cannot change payment_plan | Unable to update the property payment plan because property.status = PUBLISHED |
| INVALID_LANDLORD_PAYMENT_PLAN | Unable to proceed because paymentPlan.landlordId does not match the token.landlord |
| INVALID_EFFECTIVE_FROM_DATE | Unable to proceed because effectiveFromDate && effectiveEndDate and effectiveFromDate < now |
| INVALID_EFFECTIVE_FROM_DATE | Unable to proceed because effectiveFromDate && !effectiveEndDate and (effectiveFromDate < now || effectiveFromDate > paymentPlan.endDate) |
| INVALID_EFFECTIVE_END_DATE | Unable to proceed because effectiveFromDate && effectiveEndDate and effectiveFromDate > effectiveEndDate |
| INVALID_EFFECTIVE_END_DATE | Unable to proceed because effectiveFromDate && !effectiveEndDate and (paymentPlan.startDate > effectiveEndDate || effectiveEndDate < now) |
| only auto property can bind payment_plan | Unable to proceed because property.bookingJourney is not in [BOOK, CONNECT] |
| property and payment_plan must have the same currency | Unable to proceed because paymentPlan.currency !== property.currency |
| Only property with ap_category=key can bind deposit_is_required paymentPlan, property ${property.id} is invalid | Unable to proceed because paymentPlan.category = DEPOSIT_IS_REQUIRED and property.apCategory !== KEY |
Delete Payment Plan
-
API: deletePaymentPlan
-
Purpose & Use Cases: This API allows you to delete the payment plan
-
Validation Rule: When deleting a paymentPlan, make sure that none of the associated properties are currently published. Published properties must be unpublished before the payment plan can be deleted.
-
Request:
| Attribute Name | Data Type | Required | Details |
|---|---|---|---|
| id | ID | Yes | The unique ID of the payment plan |
QUERY
mutation DeletePaymentPlan($input: DeletePaymentPlanInput) {
deletePaymentPlan(input: $input) {
result
}
}
GRAPHQL VARIABLES
{
"input": {
"id": "eyJ0eXBlIjoiUGF5bWVudFBsYW4iLCJpZCI6MTI1fQ=="
}
}
- Response:
{
"data": {
"deletePaymentPlan": {
"result": true
}
}
}
- Error Message:
| Error Message | Description |
|---|---|
| Property is published | Unable to delete the payment plan because it is bound to a published property |
GetPaymentPlan
-
API: getPaymentPlan
-
Purpose & Use Cases: This API allows you to get single payment plan detail
-
Request:
| Attribute Name | Data Type | Required | Details |
|---|---|---|---|
| id | ID | No | The unique ID of the payment plan |
| propertyId | ID | No | The unique ID of the related property |
QUERY
query GetPaymentPlan($getPaymentPlanId: ID, $propertyId: ID) {
getPaymentPlan(id: $getPaymentPlanId, propertyId: $propertyId) {
paymentPlan {
id
landlordId
name
currency
description
startDate
endDate
createdAt
updatedAt
properties {
id
name
slug
status
bookingJourney
}
deposits {
id
paymentPlanId
name
paymentType
amount
price
currency
description
createdAt
updatedAt
}
fees {
id
paymentPlanId
name
paymentType
amount
price
currency
description
createdAt
updatedAt
}
instalments {
id
paymentPlanId
name
fromTenancy
toTenancy
tenancyUnit
fullPayment
payNowAmount
payWeekly
payRemainderAtOnce
payQuarterly
payMonthly
requireGuarantor
country {
id
name
countryCode
currency
rentCycle
}
updatedAt
createdAt
}
active
paymentOnline
category
}
}
}
GRAPHQL VARIABLES
{
"id": "eyJ0eXBlIjoiUGF5bWVudFBsYW4iLCJpZCI6NTA3fQ"
}
- Response:
{
"data": {
"getPaymentPlan": {
"paymentPlan": {
"id": "eyJ0eXBlIjoiUGF5bWVudFBsYW4iLCJpZCI6MTI1fQ==",
"landlordId": "eyJ0eXBlIjoiTGFuZGxvcmQiLCJpZCI6NDEyN30=",
"name": "open api create payment plan Test",
"currency": "GBP",
"description": "just for test",
"startDate": "2025-05-20",
"endDate": "9999-12-31",
"createdAt": "2025-05-20T06:45:16+00:00",
"updatedAt": "2025-05-29T07:48:34+00:00",
"properties": [
{
"id": "eyJ0eXBlIjoiUHJvcGVydHkiLCJpZCI6MTA0NDg4N30=",
"name": "open api create property name 02",
"slug": "open-api-create-property-name-02",
"status": "EDITING",
"bookingJourney": "MESSAGE_PROPERTY"
}
],
"deposits": [],
"fees": [
{
"id": "eyJ0eXBlIjoiT3JkZXJGZWUiLCJpZCI6MzF9",
"paymentPlanId": "eyJ0eXBlIjoiUGF5bWVudFBsYW4iLCJpZCI6MTI1fQ==",
"name": "deposit",
"paymentType": "FIXED_AMOUNT",
"amount": "88.00",
"price": null,
"currency": "GBP",
"description": null,
"createdAt": "2025-05-20T08:51:17+00:00",
"updatedAt": "2025-05-20T08:51:17+00:00"
}
],
"instalments": [
{
"id": "eyJ0eXBlIjoiT3JkZXJJbnN0YWxtZW50IiwiaWQiOjEwM30=",
"paymentPlanId": "eyJ0eXBlIjoiUGF5bWVudFBsYW4iLCJpZCI6MTI1fQ==",
"name": null,
"fromTenancy": 0,
"toTenancy": null,
"tenancyUnit": "WEEKS",
"fullPayment": true,
"payNowAmount": null,
"payWeekly": null,
"payRemainderAtOnce": null,
"payQuarterly": null,
"payMonthly": null,
"requireGuarantor": null,
"country": null,
"updatedAt": "2025-05-20T08:51:23+00:00",
"createdAt": "2025-05-20T08:51:23+00:00"
}
],
"active": true,
"paymentOnline": false,
"category": "RENT_IS_REQUIRED"
}
}
}
}
Single Property Binds Payment Plan
-
API: updatePropertyPaymentPlan
-
Purpose & Use Cases: This API allows you to bind or re-bind a single property’s payment plan
-
Request:
| Attribute Name | Data Type | Required | Details |
|---|---|---|---|
| propertyId | DecodedPropertyID | Yes | The unique ID of the related property |
| paymentPlanId | DecodedPaymentPlanID | Yes | The unique ID of the related payment plan |
QUERY
mutation UpdatePropertyPaymentPlan($input: UpdatePropertyPaymentPlanInput!) {
updatePropertyPaymentPlan(input: $input) {
success
message
}
}
GRAPHQL VARIABLES
{
"input": {
"propertyId": "eyJ0eXBlIjoiUHJvcGVydHkiLCJpZCI6MTA0NDg4OH0=",
"paymentPlanId": "eyJ0eXBlIjoiUGF5bWVudFBsYW4iLCJpZCI6MzY5fQ"
}
}
- Response:
{
"data": {
"updatePropertyPaymentPlan": {
"success": true,
"message": null
}
}
}
- Error Message:
| Error Message | Description |
|---|---|
| published property cannot change payment_plan | Cannot change payment plan because property is published |
| only auto property can bind payment_plan | Unable to proceed because property.bookingJourney is not in [BOOK, CONNECT] |
| property and payment_plan must have the same currency | Unable to proceed because paymentPlan.currency !== property.currency. |
| Only property with ap_category=key can bind deposit_is_required paymentPlan,property ${property.id} is invalid | Unable to proceed because paymentPlan.category = DEPOSIT_IS_REQUIRED and property.apCategory !== KEY |
Bind Properties To Payment Plan
-
API: bindPropertiesToPaymentPlan
-
Purpose & Use Cases: This API allows you to bind a payment plan to the corresponding properties
-
Request:
| Attribute Name | Data Type | Required | Details |
|---|---|---|---|
| id | ID | Yes | The unique ID of the payment plan |
| propertyIds | ID[] | Yes | The list of unique ID of the related property |
QUERY
mutation BindPropertiesToPaymentPlan($input: BindPropertiesToPaymentPlanInput!) {
bindPropertiesToPaymentPlan(input: $input) {
success
message
}
}
GRAPHQL VARIABLES
{
"input": {
"propertyIds": ["eyJ0eXBlIjoiUHJvcGVydHkiLCJpZCI6NTc1fQ="],
"paymentPlanId": "eyJ0eXBlIjoiUGF5bWVudFBsYW4iLCJpZCI6NTQwfQ="
}
}
- Response:
{
"data": {
"bindPropertiesToPaymentPlan": {
"success": true,
"message": null
}
}
}
- Error Message:
| Error Message | Description |
|---|---|
| The specified property could not be found | Unable to bind properties because the propertyId cannot retrieve the property |
| This property does not belong to you | Access denied because the property.landlordId does not match the token.landlord |
| Cannot bind payment plan from a published property. Please unpublish the property before proceeding | Unable to bind properties because property.status = PUBLISHED |
| Booking journey is not eligible for applying a payment plan | Unable to bind properties because property.bookingJourney is not in [BOOK, CONNECT] |
| Currency does not match the selected payment plan | Unable to bind properties because **paymentPlan.currency does not match the property.currency |
| Only property with ap_category=key can bind deposit_is_required payment plan. Property ${property.id} is invalid. | Unable to bind properties because paymentPlan.category = DEPOSIT_IS_REQUIRED and property.apCategory is not KEY |
Unbind Properties To Payment Plan
-
API: unbindPropertiesToPaymentPlan
-
Purpose & Use Cases: This API allows you to unbind a payment plan to the corresponding properties
-
Request:
| Attribute Name | Data Type | Required | Details |
|---|---|---|---|
| id | ID | Yes | The unique ID of the payment plan |
| propertyIds | ID[] | Yes | The list of unique ID of the related property |
QUERY
mutation UnbindPropertiesToPaymentPlan($input: UnbindPropertiesToPaymentPlanInput!) {
unbindPropertiesToPaymentPlan(input: $input) {
success
message
}
}
GRAPHQL VARIABLES
{
"input": {
"propertyIds": ["eyJ0eXBlIjoiUHJvcGVydHkiLCJpZCI6MTA0MjE1Nn0="],
"paymentPlanId": "eyJ0eXBlIjoiUGF5bWVudFBsYW4iLCJpZCI6MzY5fQ="
}
}
- Response:
{
"data": {
"unbindPropertiesToPaymentPlan": {
"success": true,
"message": null
}
}
}
- Error Message:
| Error Message | Description |
|---|---|
| The specified property could not be found | Unable to bind properties because the propertyId cannot retrieve the property |
| This property does not belong to you | Access denied because the property.landlordId does not match the token.landlord |
| Cannot unbind payment plan from a published property. Please unpublish the property before proceeding | Unable to bind properties because property.status = PUBLISHED |
Page Payment Plans
-
API: pagePaymentPlans
-
Purpose & Use Cases: This API allows you to get all payment plans of landlord
-
Request:
| Attribute Name | Data Type | Required | Details |
|---|---|---|---|
| pageNumber | PositiveInteger | No | The number of the page |
| pageSize | PositiveInteger | No | The size of the page |
QUERY
query GetPaymentPlans($pageNumber: PositiveInteger, $pageSize: PositiveInteger) {
pagePaymentPlans(
pageSize: $pageSize,
pageNumber: $pageNumber,
) {
paymentPlans {
id
landlordId
name
currency
description
startDate
endDate
createdAt
updatedAt
properties {
id
name
slug
status
bookingJourney
}
deposits {
id
paymentPlanId
name
paymentType
amount
price
currency
description
createdAt
updatedAt
}
fees {
id
paymentPlanId
name
paymentType
amount
price
currency
description
createdAt
updatedAt
}
instalments {
id
paymentPlanId
name
fromTenancy
toTenancy
tenancyUnit
fullPayment
payNowAmount
payWeekly
payRemainderAtOnce
payQuarterly
payMonthly
requireGuarantor
country {
id
name
countryCode
currency
rentCycle
}
updatedAt
createdAt
}
active
paymentOnline
category
}
pageInfo {
currentPage
pageSize
total
totalPages
}
}
}
GRAPHQL VARIABLES
{
"pageNumber": 1,
"pageSize": 1
}
- Response:
{
"data": {
"pagePaymentPlans": {
"paymentPlans": [
{
"id": "eyJ0eXBlIjoiUGF5bWVudFBsYW4iLCJpZCI6ODB9",
"landlordId": "eyJ0eXBlIjoiTGFuZGxvcmQiLCJpZCI6NDEyN30=",
"name": "no payment",
"currency": "USD",
"description": null,
"startDate": "2024-07-25",
"endDate": "9999-12-31",
"createdAt": "2024-07-25T07:40:17+00:00",
"updatedAt": "2024-08-20T08:21:11+00:00",
"properties": [
{
"id": "eyJ0eXBlIjoiUHJvcGVydHkiLCJpZCI6MTA0NDc5MX0=",
"name": "test___hkkkk",
"slug": "test-hkkkk",
"status": "PUBLISHED",
"bookingJourney": "MESSAGE_PROPERTY"
}
],
"deposits": [],
"fees": [],
"instalments": [
{
"id": "eyJ0eXBlIjoiT3JkZXJJbnN0YWxtZW50IiwiaWQiOjU4fQ==",
"paymentPlanId": "eyJ0eXBlIjoiUGF5bWVudFBsYW4iLCJpZCI6ODB9",
"name": null,
"fromTenancy": 0,
"toTenancy": null,
"tenancyUnit": "WEEKS",
"fullPayment": true,
"payNowAmount": null,
"payWeekly": null,
"payRemainderAtOnce": null,
"payQuarterly": null,
"payMonthly": null,
"requireGuarantor": null,
"country": null,
"updatedAt": "2024-07-25T07:40:26+00:00",
"createdAt": "2024-07-25T07:40:26+00:00"
}
],
"active": true,
"paymentOnline": false,
"category": "RENT_IS_REQUIRED"
}
],
"pageInfo": {
"currentPage": 1,
"pageSize": 1,
"total": 12,
"totalPages": 12
}
}
}
}
Payment Plan object
| Attribute Name | Data Type | Details |
|---|---|---|
| id | ID | Unique identifier for the payment plan |
| name | String | Name of the payment plan |
| currency | String | 3-letter currency code (ISO 4217), like USD or EUR |
| landlordId | ID | ID of the landlord who owns the properties |
| description | String | English description of the plan |
| startDate | Date | Start date of the plan in format ISO 8601 (YYYY-MM-DD), same as effectiveFromDate |
| endDate | Date | End date of the plan in format ISO 8601 (YYYY-MM-DD), same as effectiveEndDate |
| properties | PaymentPlanProperty[] | Property list bound to the payment plan; PaymentPlanProperty includes, id: ID, name: String, slug: String, status: LowCasePropertyStatus!, bookingJourney: BookingJourney!; LowCasePropertyStatus has possible values: NEW EDITING PUBLISHED UNPUBLISHEDBookingJourney has possible values: BOOKING_WITH_CONSULTANT BOOK CONNECT_STUDENT_SUITE CONNECT LIST |
| deposits | OrderDeposit[] | Order deposit of the payment plan; OrderDeposit is an object includes, id: ID!, paymentPlanId: ID, name: String!, paymentType: DepositFeeType, amount: NonEmptyString, price: NonEmptyString, currency: CurrencyUnit description: String, createdAt: Datetime, updatedAt: Datetime; DepositFeeType has possible values: FIXED_AMOUNT PER_BILLING_CYCLE |
| fees | OrderFee[] | Order fees of the payment plan; OrderFee is an object includes, id: ID!, paymentPlanId: ID, name: NonEmptyString!, paymentType: DepositFeeType, amount: NonEmptyString, price: NonEmptyString, currency: CurrencyUnit description: String, createdAt: Datetime, updatedAt: Datetime; DepositFeeType has possible values: FIXED_AMOUNT PER_BILLING_CYCLE |
| instalments | OrderInstalment[] | Order instalment of the payment plan; OrderInstalment is an object includes, id: ID!, paymentPlanId: ID, name: String, fromTenancy: Int, toTenancy: Int, tenancyUnit: TenancyUnit, fullPayment: Boolean, payNowAmount: Float, payWeekly: Boolean, payQuarterly: Boolean, payMonthly: Boolean, payRemainderAtOnce: Boolean requireGuarantor: Boolean, country: Country, createdAt: Datetime, updatedAt: Datetime; Country is an object includes, id: ID!, name: String!, countryCode: String!, currency: String!, rentCycle: BillingCycle!; TenancyUnit has possible values: DAYS WEEKS MONTHSBillingCycle has possible values: DAILY WEEKLY MONTHLY |