curl --request POST \
--url https://api.suby.fi/api/payment/create \
--header 'Content-Type: application/json' \
--header 'X-Suby-Api-Key: <api-key>' \
--data '
{
"productId": "pro_abc123",
"customerId": "usr_abc123",
"customerEmail": "customer@example.com",
"customerFirstName": "John",
"customerLastName": "Doe",
"priceCents": "2500",
"currency": "USD",
"discountCode": "WELCOME10",
"externalRef": "order_789",
"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": "https://your-app.com/success",
"cancelUrl": "https://your-app.com/cancel"
}
'import requests
url = "https://api.suby.fi/api/payment/create"
payload = {
"productId": "pro_abc123",
"customerId": "usr_abc123",
"customerEmail": "customer@example.com",
"customerFirstName": "John",
"customerLastName": "Doe",
"priceCents": "2500",
"currency": "USD",
"discountCode": "WELCOME10",
"externalRef": "order_789",
"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": "https://your-app.com/success",
"cancelUrl": "https://your-app.com/cancel"
}
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_abc123',
customerId: 'usr_abc123',
customerEmail: 'customer@example.com',
customerFirstName: 'John',
customerLastName: 'Doe',
priceCents: '2500',
currency: 'USD',
discountCode: 'WELCOME10',
externalRef: 'order_789',
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: 'https://your-app.com/success',
cancelUrl: 'https://your-app.com/cancel'
})
};
fetch('https://api.suby.fi/api/payment/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/payment/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_abc123',
'customerId' => 'usr_abc123',
'customerEmail' => 'customer@example.com',
'customerFirstName' => 'John',
'customerLastName' => 'Doe',
'priceCents' => '2500',
'currency' => 'USD',
'discountCode' => 'WELCOME10',
'externalRef' => 'order_789',
'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' => 'https://your-app.com/success',
'cancelUrl' => 'https://your-app.com/cancel'
]),
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/payment/create"
payload := strings.NewReader("{\n \"productId\": \"pro_abc123\",\n \"customerId\": \"usr_abc123\",\n \"customerEmail\": \"customer@example.com\",\n \"customerFirstName\": \"John\",\n \"customerLastName\": \"Doe\",\n \"priceCents\": \"2500\",\n \"currency\": \"USD\",\n \"discountCode\": \"WELCOME10\",\n \"externalRef\": \"order_789\",\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\": \"https://your-app.com/success\",\n \"cancelUrl\": \"https://your-app.com/cancel\"\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/payment/create")
.header("X-Suby-Api-Key", "<api-key>")
.header("Content-Type", "application/json")
.body("{\n \"productId\": \"pro_abc123\",\n \"customerId\": \"usr_abc123\",\n \"customerEmail\": \"customer@example.com\",\n \"customerFirstName\": \"John\",\n \"customerLastName\": \"Doe\",\n \"priceCents\": \"2500\",\n \"currency\": \"USD\",\n \"discountCode\": \"WELCOME10\",\n \"externalRef\": \"order_789\",\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\": \"https://your-app.com/success\",\n \"cancelUrl\": \"https://your-app.com/cancel\"\n}")
.asString();require 'uri'
require 'net/http'
url = URI("https://api.suby.fi/api/payment/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_abc123\",\n \"customerId\": \"usr_abc123\",\n \"customerEmail\": \"customer@example.com\",\n \"customerFirstName\": \"John\",\n \"customerLastName\": \"Doe\",\n \"priceCents\": \"2500\",\n \"currency\": \"USD\",\n \"discountCode\": \"WELCOME10\",\n \"externalRef\": \"order_789\",\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\": \"https://your-app.com/success\",\n \"cancelUrl\": \"https://your-app.com/cancel\"\n}"
response = http.request(request)
puts response.read_body{
"success": true,
"data": {
"paymentId": "pay_abc123",
"paymentUrl": "https://checkout.suby.fi/pay/pay_abc123",
"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 one-time payment
Creates a payment for a one-time product (frequencyInDays is null).
Returns a checkout URL to redirect your customer to.
For custom price products (isCustomPrice: true), provide priceCents and currency.
Subscription products? Use
POST /api/subscription/createinstead.
curl --request POST \
--url https://api.suby.fi/api/payment/create \
--header 'Content-Type: application/json' \
--header 'X-Suby-Api-Key: <api-key>' \
--data '
{
"productId": "pro_abc123",
"customerId": "usr_abc123",
"customerEmail": "customer@example.com",
"customerFirstName": "John",
"customerLastName": "Doe",
"priceCents": "2500",
"currency": "USD",
"discountCode": "WELCOME10",
"externalRef": "order_789",
"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": "https://your-app.com/success",
"cancelUrl": "https://your-app.com/cancel"
}
'import requests
url = "https://api.suby.fi/api/payment/create"
payload = {
"productId": "pro_abc123",
"customerId": "usr_abc123",
"customerEmail": "customer@example.com",
"customerFirstName": "John",
"customerLastName": "Doe",
"priceCents": "2500",
"currency": "USD",
"discountCode": "WELCOME10",
"externalRef": "order_789",
"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": "https://your-app.com/success",
"cancelUrl": "https://your-app.com/cancel"
}
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_abc123',
customerId: 'usr_abc123',
customerEmail: 'customer@example.com',
customerFirstName: 'John',
customerLastName: 'Doe',
priceCents: '2500',
currency: 'USD',
discountCode: 'WELCOME10',
externalRef: 'order_789',
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: 'https://your-app.com/success',
cancelUrl: 'https://your-app.com/cancel'
})
};
fetch('https://api.suby.fi/api/payment/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/payment/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_abc123',
'customerId' => 'usr_abc123',
'customerEmail' => 'customer@example.com',
'customerFirstName' => 'John',
'customerLastName' => 'Doe',
'priceCents' => '2500',
'currency' => 'USD',
'discountCode' => 'WELCOME10',
'externalRef' => 'order_789',
'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' => 'https://your-app.com/success',
'cancelUrl' => 'https://your-app.com/cancel'
]),
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/payment/create"
payload := strings.NewReader("{\n \"productId\": \"pro_abc123\",\n \"customerId\": \"usr_abc123\",\n \"customerEmail\": \"customer@example.com\",\n \"customerFirstName\": \"John\",\n \"customerLastName\": \"Doe\",\n \"priceCents\": \"2500\",\n \"currency\": \"USD\",\n \"discountCode\": \"WELCOME10\",\n \"externalRef\": \"order_789\",\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\": \"https://your-app.com/success\",\n \"cancelUrl\": \"https://your-app.com/cancel\"\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/payment/create")
.header("X-Suby-Api-Key", "<api-key>")
.header("Content-Type", "application/json")
.body("{\n \"productId\": \"pro_abc123\",\n \"customerId\": \"usr_abc123\",\n \"customerEmail\": \"customer@example.com\",\n \"customerFirstName\": \"John\",\n \"customerLastName\": \"Doe\",\n \"priceCents\": \"2500\",\n \"currency\": \"USD\",\n \"discountCode\": \"WELCOME10\",\n \"externalRef\": \"order_789\",\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\": \"https://your-app.com/success\",\n \"cancelUrl\": \"https://your-app.com/cancel\"\n}")
.asString();require 'uri'
require 'net/http'
url = URI("https://api.suby.fi/api/payment/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_abc123\",\n \"customerId\": \"usr_abc123\",\n \"customerEmail\": \"customer@example.com\",\n \"customerFirstName\": \"John\",\n \"customerLastName\": \"Doe\",\n \"priceCents\": \"2500\",\n \"currency\": \"USD\",\n \"discountCode\": \"WELCOME10\",\n \"externalRef\": \"order_789\",\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\": \"https://your-app.com/success\",\n \"cancelUrl\": \"https://your-app.com/cancel\"\n}"
response = http.request(request)
puts response.read_body{
"success": true,
"data": {
"paymentId": "pay_abc123",
"paymentUrl": "https://checkout.suby.fi/pay/pay_abc123",
"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 one-time product
"pro_abc123"
Optional. Links the payment 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 and 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.
"2500"
Currency for the price. Required when priceCents is provided, ignored otherwise.
USD, EUR "USD"
Optional. A discount code (created via POST /api/discount/create) to pre-apply to this checkout.
The discount is applied to the amount the customer pays. Ignored if the code is invalid,
expired, exhausted, or not attached to this product.
50"WELCOME10"
Your internal reference (order ID, invoice number, etc.)
255"order_789"
Custom key-value pairs. Returned in webhooks.
Extra fields shown on the checkout page to collect information from the customer
(e.g. Discord username, referral source, terms acceptance).
Customer responses are returned in the context.customFieldsResponse object of
every payment webhook.
Maximum 10 fields per payment. For subscriptions, fields are collected on the
initial checkout only — renewals do not re-prompt the customer, and their webhooks
will have customFields and customFieldsResponse set to null.
10Show child attributes
Show child attributes
Redirect URL after successful payment
"https://your-app.com/success"
Redirect URL if customer cancels
"https://your-app.com/cancel"
Was this page helpful?

