curl --get "${BASE_URL}/api/v2/plan/list" \
-H "Authorization: Bearer ${ACCESS_TOKEN}" \
-H "Accept: application/json" \
--data-urlencode merchant_id=48290713 \
--data-urlencode page=1 \
--data-urlencode page_size=20import json
import os
import urllib.parse
import urllib.request
base_url = os.environ["BASE_URL"].rstrip("/")
url = base_url + "/api/v2/plan/list"
headers = {"Accept": "application/json"}
headers["Authorization"] = "Bearer " + os.environ["ACCESS_TOKEN"]
params = {
"merchant_id": "48290713",
"page": "1",
"page_size": "20",
}
url += "?" + urllib.parse.urlencode(params)
body = None
request = urllib.request.Request(url, data=body, headers=headers, method="GET")
with urllib.request.urlopen(request, timeout=30) as response:
print(response.read().decode("utf-8"))
const env = (name) => {
const value = process.env[name];
if (!value) throw new Error(`Missing environment variable: ${name}`);
return value;
};
const baseUrl = env("BASE_URL").replace(/\/+$/, "");
const url = new URL(baseUrl + "/api/v2/plan/list");
const headers = { Accept: "application/json" };
headers.Authorization = `Bearer ${env("ACCESS_TOKEN")}`;
const params = new URLSearchParams({
"merchant_id": "48290713",
"page": "1",
"page_size": "20",
});
url.search = params.toString();
const response = await fetch(url, {
method: "GET",
headers,
signal: AbortSignal.timeout(30_000),
});
console.log(await response.text());
<?php
$env = static function (string $name): string {
$value = getenv($name);
if ($value === false || $value === '') {
throw new RuntimeException("Missing environment variable: " . $name);
}
return $value;
};
$baseUrl = rtrim($env('BASE_URL'), '/');
$url = $baseUrl . "/api/v2/plan/list";
$headers = ['Accept: application/json'];
$headers[] = 'Authorization: Bearer ' . $env('ACCESS_TOKEN');
$params = [
"merchant_id" => "48290713",
"page" => "1",
"page_size" => "20",
];
$url .= '?' . http_build_query($params, '', '&', PHP_QUERY_RFC3986);
$curl = curl_init($url);
curl_setopt_array($curl, [
CURLOPT_CUSTOMREQUEST => "GET",
CURLOPT_HTTPHEADER => $headers,
CURLOPT_RETURNTRANSFER => true,
CURLOPT_TIMEOUT => 30,
]);
$response = curl_exec($curl);
if ($response === false) {
$error = curl_error($curl);
curl_close($curl);
throw new RuntimeException($error);
}
curl_close($curl);
echo $response, PHP_EOL;
package main
import (
"fmt"
"io"
"net/http"
"net/url"
"os"
"strings"
"time"
)
func mustEnv(name string) string {
value := os.Getenv(name)
if value == "" { panic("Missing environment variable: " + name) }
return value
}
func main() {
baseURL := strings.TrimRight(mustEnv("BASE_URL"), "/")
endpoint := baseURL + "/api/v2/plan/list"
params := url.Values{}
params.Set("merchant_id", "48290713")
params.Set("page", "1")
params.Set("page_size", "20")
endpoint += "?" + params.Encode()
req, err := http.NewRequest("GET", endpoint, nil)
if err != nil { panic(err) }
req.Header.Set("Accept", "application/json")
req.Header.Set("Authorization", "Bearer " + mustEnv("ACCESS_TOKEN"))
client := &http.Client{Timeout: 30 * time.Second}
response, err := client.Do(req)
if err != nil { panic(err) }
defer response.Body.Close()
result, err := io.ReadAll(response.Body)
if err != nil { panic(err) }
fmt.Println(string(result))
}
import java.io.ByteArrayOutputStream;
import java.net.URI;
import java.net.URLEncoder;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Path;
import java.time.Duration;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.UUID;
import java.util.stream.Collectors;
public class Example {
static String requireEnv(String name) {
String value = System.getenv(name);
if (value == null || value.isEmpty()) {
throw new IllegalArgumentException("Missing environment variable: " + name);
}
return value;
}
public static void main(String[] args) throws Exception {
String baseUrl = requireEnv("BASE_URL").replaceAll("/+$", "");
String endpoint = baseUrl + "/api/v2/plan/list";
Map<String, String> params = new LinkedHashMap<>();
params.put("merchant_id", "48290713");
params.put("page", "1");
params.put("page_size", "20");
String encoded = params.entrySet().stream()
.map(e -> URLEncoder.encode(e.getKey(), StandardCharsets.UTF_8) + "="
+ URLEncoder.encode(e.getValue(), StandardCharsets.UTF_8))
.collect(Collectors.joining("&"));
endpoint += "?" + encoded;
HttpRequest.Builder builder = HttpRequest.newBuilder(URI.create(endpoint))
.timeout(Duration.ofSeconds(30))
.header("Accept", "application/json");
builder.header("Authorization", "Bearer " + requireEnv("ACCESS_TOKEN"));
HttpRequest request = builder.method("GET", HttpRequest.BodyPublishers.noBody()).build();
HttpClient client = HttpClient.newBuilder().connectTimeout(Duration.ofSeconds(30)).build();
HttpResponse<String> response = client.send(request,
HttpResponse.BodyHandlers.ofString(StandardCharsets.UTF_8));
System.out.println(response.body());
}
}
require "net/http"
require "uri"
base_url = ENV.fetch("BASE_URL").sub(%r{/+$}, "")
uri = URI(base_url + "/api/v2/plan/list")
params = {
"merchant_id" => "48290713",
"page" => "1",
"page_size" => "20",
}
uri.query = URI.encode_www_form(params)
request = Net::HTTP::Get.new(uri)
request["Accept"] = "application/json"
request["Authorization"] = "Bearer " + ENV.fetch("ACCESS_TOKEN")
http = Net::HTTP.new(uri.host, uri.port)
http.use_ssl = uri.scheme == "https"
http.open_timeout = 30
http.read_timeout = 30
puts http.request(request).body
{
"code": 0,
"message": "ok",
"data": {
"data": [
{
"plan_id": "TPL62038591",
"plan_name": "Monthly membership",
"description": "Monthly subscription template",
"status": 1,
"invoice_account_code": null,
"invoice_theme_id": null,
"surcharge_config": {
"direct_debit_surcharge": false,
"visa_domestic_surcharge": false,
"visa_international_surcharge": false,
"mastercard_domestic_surcharge": false,
"mastercard_international_surcharge": false,
"amex_domestic_surcharge": false,
"amex_international_surcharge": false
},
"surcharge_config_enabled": [],
"surcharge_config_selected": [],
"recurring": {
"enabled": 1,
"schedule_name": "Monthly fee",
"recurring_type": 3,
"amount": "25.50",
"total_amount": null,
"start_date": null,
"end_date": null,
"end_terms": null,
"day_of_debit": "FREQ=MONTHLY;INTERVAL=1;BYMONTHDAY=15",
"day_of_debit_dtstart": null,
"process_type": 5,
"custom_cycle_type": 0,
"custom_cycle": 0,
"custom_cycle_type_text": "",
"frequency": "Monthly"
},
"one_off": {
"enabled": 0,
"schedule_name": null,
"amount": null,
"description": null,
"payment_date": null
},
"created_at": "2026-10-28 09:00:00",
"updated_at": "2026-10-28 09:00:00",
"deleted_at": null
}
],
"current_page": 1,
"per_page": 20,
"total": 1
}
}List available subscription templates
Lists subscription templates available to the authorized merchant and specified business. Only ACTIVE, enabled, non-tiered templates other than By agreement are returned, ordered by creation time descending. Price filters apply to the latest template price. Callers must ensure each range starts at or below its end.
The list provides candidate templates. Subscription creation still validates pricing, enabled charge segments and required dates, and returns a business error if the selected template cannot be used.
All examples illustrate request and response formats. Replace customers, orders, cards and accounts with approved test data. BASE_URL is the service root domain without /api/v2. ACCESS_TOKEN is the access_token returned by /oauth/token. Example dates are in 2030; replace them with actual dates that satisfy the current date restrictions.
curl --get "${BASE_URL}/api/v2/plan/list" \
-H "Authorization: Bearer ${ACCESS_TOKEN}" \
-H "Accept: application/json" \
--data-urlencode merchant_id=48290713 \
--data-urlencode page=1 \
--data-urlencode page_size=20import json
import os
import urllib.parse
import urllib.request
base_url = os.environ["BASE_URL"].rstrip("/")
url = base_url + "/api/v2/plan/list"
headers = {"Accept": "application/json"}
headers["Authorization"] = "Bearer " + os.environ["ACCESS_TOKEN"]
params = {
"merchant_id": "48290713",
"page": "1",
"page_size": "20",
}
url += "?" + urllib.parse.urlencode(params)
body = None
request = urllib.request.Request(url, data=body, headers=headers, method="GET")
with urllib.request.urlopen(request, timeout=30) as response:
print(response.read().decode("utf-8"))
const env = (name) => {
const value = process.env[name];
if (!value) throw new Error(`Missing environment variable: ${name}`);
return value;
};
const baseUrl = env("BASE_URL").replace(/\/+$/, "");
const url = new URL(baseUrl + "/api/v2/plan/list");
const headers = { Accept: "application/json" };
headers.Authorization = `Bearer ${env("ACCESS_TOKEN")}`;
const params = new URLSearchParams({
"merchant_id": "48290713",
"page": "1",
"page_size": "20",
});
url.search = params.toString();
const response = await fetch(url, {
method: "GET",
headers,
signal: AbortSignal.timeout(30_000),
});
console.log(await response.text());
<?php
$env = static function (string $name): string {
$value = getenv($name);
if ($value === false || $value === '') {
throw new RuntimeException("Missing environment variable: " . $name);
}
return $value;
};
$baseUrl = rtrim($env('BASE_URL'), '/');
$url = $baseUrl . "/api/v2/plan/list";
$headers = ['Accept: application/json'];
$headers[] = 'Authorization: Bearer ' . $env('ACCESS_TOKEN');
$params = [
"merchant_id" => "48290713",
"page" => "1",
"page_size" => "20",
];
$url .= '?' . http_build_query($params, '', '&', PHP_QUERY_RFC3986);
$curl = curl_init($url);
curl_setopt_array($curl, [
CURLOPT_CUSTOMREQUEST => "GET",
CURLOPT_HTTPHEADER => $headers,
CURLOPT_RETURNTRANSFER => true,
CURLOPT_TIMEOUT => 30,
]);
$response = curl_exec($curl);
if ($response === false) {
$error = curl_error($curl);
curl_close($curl);
throw new RuntimeException($error);
}
curl_close($curl);
echo $response, PHP_EOL;
package main
import (
"fmt"
"io"
"net/http"
"net/url"
"os"
"strings"
"time"
)
func mustEnv(name string) string {
value := os.Getenv(name)
if value == "" { panic("Missing environment variable: " + name) }
return value
}
func main() {
baseURL := strings.TrimRight(mustEnv("BASE_URL"), "/")
endpoint := baseURL + "/api/v2/plan/list"
params := url.Values{}
params.Set("merchant_id", "48290713")
params.Set("page", "1")
params.Set("page_size", "20")
endpoint += "?" + params.Encode()
req, err := http.NewRequest("GET", endpoint, nil)
if err != nil { panic(err) }
req.Header.Set("Accept", "application/json")
req.Header.Set("Authorization", "Bearer " + mustEnv("ACCESS_TOKEN"))
client := &http.Client{Timeout: 30 * time.Second}
response, err := client.Do(req)
if err != nil { panic(err) }
defer response.Body.Close()
result, err := io.ReadAll(response.Body)
if err != nil { panic(err) }
fmt.Println(string(result))
}
import java.io.ByteArrayOutputStream;
import java.net.URI;
import java.net.URLEncoder;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Path;
import java.time.Duration;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.UUID;
import java.util.stream.Collectors;
public class Example {
static String requireEnv(String name) {
String value = System.getenv(name);
if (value == null || value.isEmpty()) {
throw new IllegalArgumentException("Missing environment variable: " + name);
}
return value;
}
public static void main(String[] args) throws Exception {
String baseUrl = requireEnv("BASE_URL").replaceAll("/+$", "");
String endpoint = baseUrl + "/api/v2/plan/list";
Map<String, String> params = new LinkedHashMap<>();
params.put("merchant_id", "48290713");
params.put("page", "1");
params.put("page_size", "20");
String encoded = params.entrySet().stream()
.map(e -> URLEncoder.encode(e.getKey(), StandardCharsets.UTF_8) + "="
+ URLEncoder.encode(e.getValue(), StandardCharsets.UTF_8))
.collect(Collectors.joining("&"));
endpoint += "?" + encoded;
HttpRequest.Builder builder = HttpRequest.newBuilder(URI.create(endpoint))
.timeout(Duration.ofSeconds(30))
.header("Accept", "application/json");
builder.header("Authorization", "Bearer " + requireEnv("ACCESS_TOKEN"));
HttpRequest request = builder.method("GET", HttpRequest.BodyPublishers.noBody()).build();
HttpClient client = HttpClient.newBuilder().connectTimeout(Duration.ofSeconds(30)).build();
HttpResponse<String> response = client.send(request,
HttpResponse.BodyHandlers.ofString(StandardCharsets.UTF_8));
System.out.println(response.body());
}
}
require "net/http"
require "uri"
base_url = ENV.fetch("BASE_URL").sub(%r{/+$}, "")
uri = URI(base_url + "/api/v2/plan/list")
params = {
"merchant_id" => "48290713",
"page" => "1",
"page_size" => "20",
}
uri.query = URI.encode_www_form(params)
request = Net::HTTP::Get.new(uri)
request["Accept"] = "application/json"
request["Authorization"] = "Bearer " + ENV.fetch("ACCESS_TOKEN")
http = Net::HTTP.new(uri.host, uri.port)
http.use_ssl = uri.scheme == "https"
http.open_timeout = 30
http.read_timeout = 30
puts http.request(request).body
{
"code": 0,
"message": "ok",
"data": {
"data": [
{
"plan_id": "TPL62038591",
"plan_name": "Monthly membership",
"description": "Monthly subscription template",
"status": 1,
"invoice_account_code": null,
"invoice_theme_id": null,
"surcharge_config": {
"direct_debit_surcharge": false,
"visa_domestic_surcharge": false,
"visa_international_surcharge": false,
"mastercard_domestic_surcharge": false,
"mastercard_international_surcharge": false,
"amex_domestic_surcharge": false,
"amex_international_surcharge": false
},
"surcharge_config_enabled": [],
"surcharge_config_selected": [],
"recurring": {
"enabled": 1,
"schedule_name": "Monthly fee",
"recurring_type": 3,
"amount": "25.50",
"total_amount": null,
"start_date": null,
"end_date": null,
"end_terms": null,
"day_of_debit": "FREQ=MONTHLY;INTERVAL=1;BYMONTHDAY=15",
"day_of_debit_dtstart": null,
"process_type": 5,
"custom_cycle_type": 0,
"custom_cycle": 0,
"custom_cycle_type_text": "",
"frequency": "Monthly"
},
"one_off": {
"enabled": 0,
"schedule_name": null,
"amount": null,
"description": null,
"payment_date": null
},
"created_at": "2026-10-28 09:00:00",
"updated_at": "2026-10-28 09:00:00",
"deleted_at": null
}
],
"current_page": 1,
"per_page": 20,
"total": 1
}
}Authorizations
OAuth2 client_credentials authorization. Obtain a new access_token when it expires.
Query Parameters
Required business identifier assigned by BillBuddy, up to 20 characters; must belong to the merchant associated with the OAuth credentials.
20Page number. Defaults to 1.
x >= 1Items per page. Defaults to 20; no maximum is specified.
x >= 1Partial match on the template name.
255Template debit frequency: 1 One-off, 3 Weekly, 4 Fortnightly, 5 Monthly, 6 Quarterly, 7 Half Yearly, 8 Yearly, 12 Custom.
1, 3, 4, 5, 6, 7, 8, 12, null Minimum latest template price. Amounts use major currency units, for example 10.50 means AUD 10.50, with at most two decimal places. Numbers and numeric strings are accepted, with a minimum of 0; the same range applies to both types.
x >= 0Must be a multiple of 0.01Maximum latest template price. Amounts use major currency units, for example 10.50 means AUD 10.50, with at most two decimal places. Numbers and numeric strings are accepted, with a minimum of 0; the same range applies to both types.
x >= 0Must be a multiple of 0.01Creation date range start, inclusive from 00:00:00. Must use YYYY-MM-DD.
Creation date range end, inclusive through 23:59:59. Must use YYYY-MM-DD.
Update date range start, inclusive from 00:00:00. Must use YYYY-MM-DD.
Update date range end, inclusive through 23:59:59. Must use YYYY-MM-DD.
Response
HTTP 200. Distinguish success, business errors, validation errors and business authentication errors using the response body code.