curl --request POST \
--url https://api.suby.fi/api/subscription/create \
--header 'Content-Type: application/json' \
--header 'X-Suby-Api-Key: <api-key>' \
--data '
{
"productId": "pro_sub456",
"customerId": "usr_abc123",
"customerEmail": "customer@example.com",
"customerFirstName": "John",
"customerLastName": "Doe",
"priceCents": "999",
"discountCode": "WELCOME10",
"externalRef": "sub_ref_001",
"metadata": {},
"customFields": [
{
"key": "discord_username",
"label": "Discord username",
"required": false,
"placeholder": "e.g. test#1234",
"options": [
{
"value": "twitter",
"label": "Twitter / X"
}
],
"validation": {
"regex": "^.{2,32}$",
"errorMessage": "Please enter a valid Discord username"
}
}
],
"successUrl": "<string>",
"cancelUrl": "<string>"
}
'import requests
url = "https://api.suby.fi/api/subscription/create"
payload = {
"productId": "pro_sub456",
"customerId": "usr_abc123",
"customerEmail": "customer@example.com",
"customerFirstName": "John",
"customerLastName": "Doe",
"priceCents": "999",
"discountCode": "WELCOME10",
"externalRef": "sub_ref_001",
"metadata": {},
"customFields": [
{
"key": "discord_username",
"label": "Discord username",
"required": False,
"placeholder": "e.g. test#1234",
"options": [
{
"value": "twitter",
"label": "Twitter / X"
}
],
"validation": {
"regex": "^.{2,32}$",
"errorMessage": "Please enter a valid Discord username"
}
}
],
"successUrl": "<string>",
"cancelUrl": "<string>"
}
headers = {
"X-Suby-Api-Key": "<api-key>",
"Content-Type": "application/json"
}
response = requests.post(url, json=payload, headers=headers)
print(response.text)const options = {
method: 'POST',
headers: {'X-Suby-Api-Key': '<api-key>', 'Content-Type': 'application/json'},
body: JSON.stringify({
productId: 'pro_sub456',
customerId: 'usr_abc123',
customerEmail: 'customer@example.com',
customerFirstName: 'John',
customerLastName: 'Doe',
priceCents: '999',
discountCode: 'WELCOME10',
externalRef: 'sub_ref_001',
metadata: {},
customFields: [
{
key: 'discord_username',
label: 'Discord username',
required: false,
placeholder: 'e.g. test#1234',
options: [{value: 'twitter', label: 'Twitter / X'}],
validation: {regex: '^.{2,32}$', errorMessage: 'Please enter a valid Discord username'}
}
],
successUrl: '<string>',
cancelUrl: '<string>'
})
};
fetch('https://api.suby.fi/api/subscription/create', options)
.then(res => res.json())
.then(res => console.log(res))
.catch(err => console.error(err));<?php
$curl = curl_init();
curl_setopt_array($curl, [
CURLOPT_URL => "https://api.suby.fi/api/subscription/create",
CURLOPT_RETURNTRANSFER => true,
CURLOPT_ENCODING => "",
CURLOPT_MAXREDIRS => 10,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
CURLOPT_CUSTOMREQUEST => "POST",
CURLOPT_POSTFIELDS => json_encode([
'productId' => 'pro_sub456',
'customerId' => 'usr_abc123',
'customerEmail' => 'customer@example.com',
'customerFirstName' => 'John',
'customerLastName' => 'Doe',
'priceCents' => '999',
'discountCode' => 'WELCOME10',
'externalRef' => 'sub_ref_001',
'metadata' => [
],
'customFields' => [
[
'key' => 'discord_username',
'label' => 'Discord username',
'required' => false,
'placeholder' => 'e.g. test#1234',
'options' => [
[
'value' => 'twitter',
'label' => 'Twitter / X'
]
],
'validation' => [
'regex' => '^.{2,32}$',
'errorMessage' => 'Please enter a valid Discord username'
]
]
],
'successUrl' => '<string>',
'cancelUrl' => '<string>'
]),
CURLOPT_HTTPHEADER => [
"Content-Type: application/json",
"X-Suby-Api-Key: <api-key>"
],
]);
$response = curl_exec($curl);
$err = curl_error($curl);
curl_close($curl);
if ($err) {
echo "cURL Error #:" . $err;
} else {
echo $response;
}package main
import (
"fmt"
"strings"
"net/http"
"io"
)
func main() {
url := "https://api.suby.fi/api/subscription/create"
payload := strings.NewReader("{\n \"productId\": \"pro_sub456\",\n \"customerId\": \"usr_abc123\",\n \"customerEmail\": \"customer@example.com\",\n \"customerFirstName\": \"John\",\n \"customerLastName\": \"Doe\",\n \"priceCents\": \"999\",\n \"discountCode\": \"WELCOME10\",\n \"externalRef\": \"sub_ref_001\",\n \"metadata\": {},\n \"customFields\": [\n {\n \"key\": \"discord_username\",\n \"label\": \"Discord username\",\n \"required\": false,\n \"placeholder\": \"e.g. test#1234\",\n \"options\": [\n {\n \"value\": \"twitter\",\n \"label\": \"Twitter / X\"\n }\n ],\n \"validation\": {\n \"regex\": \"^.{2,32}$\",\n \"errorMessage\": \"Please enter a valid Discord username\"\n }\n }\n ],\n \"successUrl\": \"<string>\",\n \"cancelUrl\": \"<string>\"\n}")
req, _ := http.NewRequest("POST", url, payload)
req.Header.Add("X-Suby-Api-Key", "<api-key>")
req.Header.Add("Content-Type", "application/json")
res, _ := http.DefaultClient.Do(req)
defer res.Body.Close()
body, _ := io.ReadAll(res.Body)
fmt.Println(string(body))
}HttpResponse<String> response = Unirest.post("https://api.suby.fi/api/subscription/create")
.header("X-Suby-Api-Key", "<api-key>")
.header("Content-Type", "application/json")
.body("{\n \"productId\": \"pro_sub456\",\n \"customerId\": \"usr_abc123\",\n \"customerEmail\": \"customer@example.com\",\n \"customerFirstName\": \"John\",\n \"customerLastName\": \"Doe\",\n \"priceCents\": \"999\",\n \"discountCode\": \"WELCOME10\",\n \"externalRef\": \"sub_ref_001\",\n \"metadata\": {},\n \"customFields\": [\n {\n \"key\": \"discord_username\",\n \"label\": \"Discord username\",\n \"required\": false,\n \"placeholder\": \"e.g. test#1234\",\n \"options\": [\n {\n \"value\": \"twitter\",\n \"label\": \"Twitter / X\"\n }\n ],\n \"validation\": {\n \"regex\": \"^.{2,32}$\",\n \"errorMessage\": \"Please enter a valid Discord username\"\n }\n }\n ],\n \"successUrl\": \"<string>\",\n \"cancelUrl\": \"<string>\"\n}")
.asString();require 'uri'
require 'net/http'
url = URI("https://api.suby.fi/api/subscription/create")
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
request = Net::HTTP::Post.new(url)
request["X-Suby-Api-Key"] = '<api-key>'
request["Content-Type"] = 'application/json'
request.body = "{\n \"productId\": \"pro_sub456\",\n \"customerId\": \"usr_abc123\",\n \"customerEmail\": \"customer@example.com\",\n \"customerFirstName\": \"John\",\n \"customerLastName\": \"Doe\",\n \"priceCents\": \"999\",\n \"discountCode\": \"WELCOME10\",\n \"externalRef\": \"sub_ref_001\",\n \"metadata\": {},\n \"customFields\": [\n {\n \"key\": \"discord_username\",\n \"label\": \"Discord username\",\n \"required\": false,\n \"placeholder\": \"e.g. test#1234\",\n \"options\": [\n {\n \"value\": \"twitter\",\n \"label\": \"Twitter / X\"\n }\n ],\n \"validation\": {\n \"regex\": \"^.{2,32}$\",\n \"errorMessage\": \"Please enter a valid Discord username\"\n }\n }\n ],\n \"successUrl\": \"<string>\",\n \"cancelUrl\": \"<string>\"\n}"
response = http.request(request)
puts response.read_body{
"success": true,
"data": {
"paymentId": "pay_sub789",
"paymentUrl": "https://checkout.suby.fi/sub/pay_sub789",
"metadata": {},
"customFields": [
{
"key": "discord_username",
"label": "Discord username",
"required": false,
"placeholder": "e.g. test#1234",
"options": [
{
"value": "twitter",
"label": "Twitter / X"
}
],
"validation": {
"regex": "^.{2,32}$",
"errorMessage": "Please enter a valid Discord username"
}
}
]
}
}{
"success": false,
"error": {
"code": "PRODUCT_NOT_FOUND",
"message": "Product not found"
}
}{
"success": false,
"error": {
"code": "UNAUTHORIZED",
"message": "Invalid or missing API key"
}
}{
"success": false,
"error": {
"code": "NOT_FOUND",
"message": "Resource not found"
}
}Create a subscription payment
Creates a subscription payment for a recurring product (frequencyInDays is set).
Returns a checkout URL to redirect your customer to.
For custom price products (isCustomPrice: true), provide priceCents and currency.
One-time products? Use
POST /api/payment/createinstead.
curl --request POST \
--url https://api.suby.fi/api/subscription/create \
--header 'Content-Type: application/json' \
--header 'X-Suby-Api-Key: <api-key>' \
--data '
{
"productId": "pro_sub456",
"customerId": "usr_abc123",
"customerEmail": "customer@example.com",
"customerFirstName": "John",
"customerLastName": "Doe",
"priceCents": "999",
"discountCode": "WELCOME10",
"externalRef": "sub_ref_001",
"metadata": {},
"customFields": [
{
"key": "discord_username",
"label": "Discord username",
"required": false,
"placeholder": "e.g. test#1234",
"options": [
{
"value": "twitter",
"label": "Twitter / X"
}
],
"validation": {
"regex": "^.{2,32}$",
"errorMessage": "Please enter a valid Discord username"
}
}
],
"successUrl": "<string>",
"cancelUrl": "<string>"
}
'import requests
url = "https://api.suby.fi/api/subscription/create"
payload = {
"productId": "pro_sub456",
"customerId": "usr_abc123",
"customerEmail": "customer@example.com",
"customerFirstName": "John",
"customerLastName": "Doe",
"priceCents": "999",
"discountCode": "WELCOME10",
"externalRef": "sub_ref_001",
"metadata": {},
"customFields": [
{
"key": "discord_username",
"label": "Discord username",
"required": False,
"placeholder": "e.g. test#1234",
"options": [
{
"value": "twitter",
"label": "Twitter / X"
}
],
"validation": {
"regex": "^.{2,32}$",
"errorMessage": "Please enter a valid Discord username"
}
}
],
"successUrl": "<string>",
"cancelUrl": "<string>"
}
headers = {
"X-Suby-Api-Key": "<api-key>",
"Content-Type": "application/json"
}
response = requests.post(url, json=payload, headers=headers)
print(response.text)const options = {
method: 'POST',
headers: {'X-Suby-Api-Key': '<api-key>', 'Content-Type': 'application/json'},
body: JSON.stringify({
productId: 'pro_sub456',
customerId: 'usr_abc123',
customerEmail: 'customer@example.com',
customerFirstName: 'John',
customerLastName: 'Doe',
priceCents: '999',
discountCode: 'WELCOME10',
externalRef: 'sub_ref_001',
metadata: {},
customFields: [
{
key: 'discord_username',
label: 'Discord username',
required: false,
placeholder: 'e.g. test#1234',
options: [{value: 'twitter', label: 'Twitter / X'}],
validation: {regex: '^.{2,32}$', errorMessage: 'Please enter a valid Discord username'}
}
],
successUrl: '<string>',
cancelUrl: '<string>'
})
};
fetch('https://api.suby.fi/api/subscription/create', options)
.then(res => res.json())
.then(res => console.log(res))
.catch(err => console.error(err));<?php
$curl = curl_init();
curl_setopt_array($curl, [
CURLOPT_URL => "https://api.suby.fi/api/subscription/create",
CURLOPT_RETURNTRANSFER => true,
CURLOPT_ENCODING => "",
CURLOPT_MAXREDIRS => 10,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
CURLOPT_CUSTOMREQUEST => "POST",
CURLOPT_POSTFIELDS => json_encode([
'productId' => 'pro_sub456',
'customerId' => 'usr_abc123',
'customerEmail' => 'customer@example.com',
'customerFirstName' => 'John',
'customerLastName' => 'Doe',
'priceCents' => '999',
'discountCode' => 'WELCOME10',
'externalRef' => 'sub_ref_001',
'metadata' => [
],
'customFields' => [
[
'key' => 'discord_username',
'label' => 'Discord username',
'required' => false,
'placeholder' => 'e.g. test#1234',
'options' => [
[
'value' => 'twitter',
'label' => 'Twitter / X'
]
],
'validation' => [
'regex' => '^.{2,32}$',
'errorMessage' => 'Please enter a valid Discord username'
]
]
],
'successUrl' => '<string>',
'cancelUrl' => '<string>'
]),
CURLOPT_HTTPHEADER => [
"Content-Type: application/json",
"X-Suby-Api-Key: <api-key>"
],
]);
$response = curl_exec($curl);
$err = curl_error($curl);
curl_close($curl);
if ($err) {
echo "cURL Error #:" . $err;
} else {
echo $response;
}package main
import (
"fmt"
"strings"
"net/http"
"io"
)
func main() {
url := "https://api.suby.fi/api/subscription/create"
payload := strings.NewReader("{\n \"productId\": \"pro_sub456\",\n \"customerId\": \"usr_abc123\",\n \"customerEmail\": \"customer@example.com\",\n \"customerFirstName\": \"John\",\n \"customerLastName\": \"Doe\",\n \"priceCents\": \"999\",\n \"discountCode\": \"WELCOME10\",\n \"externalRef\": \"sub_ref_001\",\n \"metadata\": {},\n \"customFields\": [\n {\n \"key\": \"discord_username\",\n \"label\": \"Discord username\",\n \"required\": false,\n \"placeholder\": \"e.g. test#1234\",\n \"options\": [\n {\n \"value\": \"twitter\",\n \"label\": \"Twitter / X\"\n }\n ],\n \"validation\": {\n \"regex\": \"^.{2,32}$\",\n \"errorMessage\": \"Please enter a valid Discord username\"\n }\n }\n ],\n \"successUrl\": \"<string>\",\n \"cancelUrl\": \"<string>\"\n}")
req, _ := http.NewRequest("POST", url, payload)
req.Header.Add("X-Suby-Api-Key", "<api-key>")
req.Header.Add("Content-Type", "application/json")
res, _ := http.DefaultClient.Do(req)
defer res.Body.Close()
body, _ := io.ReadAll(res.Body)
fmt.Println(string(body))
}HttpResponse<String> response = Unirest.post("https://api.suby.fi/api/subscription/create")
.header("X-Suby-Api-Key", "<api-key>")
.header("Content-Type", "application/json")
.body("{\n \"productId\": \"pro_sub456\",\n \"customerId\": \"usr_abc123\",\n \"customerEmail\": \"customer@example.com\",\n \"customerFirstName\": \"John\",\n \"customerLastName\": \"Doe\",\n \"priceCents\": \"999\",\n \"discountCode\": \"WELCOME10\",\n \"externalRef\": \"sub_ref_001\",\n \"metadata\": {},\n \"customFields\": [\n {\n \"key\": \"discord_username\",\n \"label\": \"Discord username\",\n \"required\": false,\n \"placeholder\": \"e.g. test#1234\",\n \"options\": [\n {\n \"value\": \"twitter\",\n \"label\": \"Twitter / X\"\n }\n ],\n \"validation\": {\n \"regex\": \"^.{2,32}$\",\n \"errorMessage\": \"Please enter a valid Discord username\"\n }\n }\n ],\n \"successUrl\": \"<string>\",\n \"cancelUrl\": \"<string>\"\n}")
.asString();require 'uri'
require 'net/http'
url = URI("https://api.suby.fi/api/subscription/create")
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
request = Net::HTTP::Post.new(url)
request["X-Suby-Api-Key"] = '<api-key>'
request["Content-Type"] = 'application/json'
request.body = "{\n \"productId\": \"pro_sub456\",\n \"customerId\": \"usr_abc123\",\n \"customerEmail\": \"customer@example.com\",\n \"customerFirstName\": \"John\",\n \"customerLastName\": \"Doe\",\n \"priceCents\": \"999\",\n \"discountCode\": \"WELCOME10\",\n \"externalRef\": \"sub_ref_001\",\n \"metadata\": {},\n \"customFields\": [\n {\n \"key\": \"discord_username\",\n \"label\": \"Discord username\",\n \"required\": false,\n \"placeholder\": \"e.g. test#1234\",\n \"options\": [\n {\n \"value\": \"twitter\",\n \"label\": \"Twitter / X\"\n }\n ],\n \"validation\": {\n \"regex\": \"^.{2,32}$\",\n \"errorMessage\": \"Please enter a valid Discord username\"\n }\n }\n ],\n \"successUrl\": \"<string>\",\n \"cancelUrl\": \"<string>\"\n}"
response = http.request(request)
puts response.read_body{
"success": true,
"data": {
"paymentId": "pay_sub789",
"paymentUrl": "https://checkout.suby.fi/sub/pay_sub789",
"metadata": {},
"customFields": [
{
"key": "discord_username",
"label": "Discord username",
"required": false,
"placeholder": "e.g. test#1234",
"options": [
{
"value": "twitter",
"label": "Twitter / X"
}
],
"validation": {
"regex": "^.{2,32}$",
"errorMessage": "Please enter a valid Discord username"
}
}
]
}
}{
"success": false,
"error": {
"code": "PRODUCT_NOT_FOUND",
"message": "Product not found"
}
}{
"success": false,
"error": {
"code": "UNAUTHORIZED",
"message": "Invalid or missing API key"
}
}{
"success": false,
"error": {
"code": "NOT_FOUND",
"message": "Resource not found"
}
}Authorizations
API key authentication
Body
ID of a subscription product
"pro_sub456"
Optional. Links the subscription to an existing customer by their stable id (as returned by
GET /api/customer and on payment responses/webhooks). Keeps the same customer linked even if
their email has changed. Takes precedence over customerEmail. Returns 404 if unknown.
255"usr_abc123"
Optional. If provided (and no customerId is given), a user account is created (or reused by
email) immediately and linked to the payment. If both are omitted, the payment is created without
a customer; the email is collected on the hosted checkout page.
"customer@example.com"
Optional. Customer first name. Backfills the display name when the customer has none. Ignored when neither customerId nor customerEmail is provided.
100"John"
Optional. Customer last name. Backfills the display name when the customer has none. Ignored when neither customerId nor customerEmail is provided.
100"Doe"
Price in cents as a string.
Required when the product has isCustomPrice: true. Must NOT be provided for fixed-price products.
This price is locked for all future renewals.
"999"
Currency for the price. Required when priceCents is provided, ignored otherwise.
USD, EUR Optional. A discount code (created via POST /api/discount/create) to pre-apply to this
subscription checkout. Applied to the amount the customer pays. Ignored if the code is invalid,
expired, exhausted, or not attached to this product.
50"WELCOME10"
255"sub_ref_001"
Extra fields shown on the checkout page to collect information from the customer
(e.g. Discord username, referral source, terms acceptance).
Fields are collected on the initial checkout only — renewal payments do not
re-prompt the customer. The initial payment webhook carries the responses in
context.customFieldsResponse; renewal webhooks have it set to null.
Maximum 10 fields per subscription.
10Show child attributes
Show child attributes
Was this page helpful?

