Migrate a subscription to a new plan
curl --request POST \
--url https://billing.funnelfox.com/{org_id}/v1/subscription/migration \
--header 'Content-Type: application/json' \
--header 'ff-secret-key: <api-key>' \
--data '
{
"subs_id": "3c90c3cc-0d44-4b50-8888-8dd25736052a",
"pp_ident": "<string>",
"reason": "",
"comment": "",
"external_id": "<string>",
"strategy": "price_prorate",
"dry_run": false,
"strict_mode": true
}
'import requests
url = "https://billing.funnelfox.com/{org_id}/v1/subscription/migration"
payload = {
"subs_id": "3c90c3cc-0d44-4b50-8888-8dd25736052a",
"pp_ident": "<string>",
"reason": "",
"comment": "",
"external_id": "<string>",
"strategy": "price_prorate",
"dry_run": False,
"strict_mode": True
}
headers = {
"ff-secret-key": "<api-key>",
"Content-Type": "application/json"
}
response = requests.post(url, json=payload, headers=headers)
print(response.text)const options = {
method: 'POST',
headers: {'ff-secret-key': '<api-key>', 'Content-Type': 'application/json'},
body: JSON.stringify({
subs_id: '3c90c3cc-0d44-4b50-8888-8dd25736052a',
pp_ident: '<string>',
reason: '',
comment: '',
external_id: '<string>',
strategy: 'price_prorate',
dry_run: false,
strict_mode: true
})
};
fetch('https://billing.funnelfox.com/{org_id}/v1/subscription/migration', 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://billing.funnelfox.com/{org_id}/v1/subscription/migration",
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([
'subs_id' => '3c90c3cc-0d44-4b50-8888-8dd25736052a',
'pp_ident' => '<string>',
'reason' => '',
'comment' => '',
'external_id' => '<string>',
'strategy' => 'price_prorate',
'dry_run' => false,
'strict_mode' => true
]),
CURLOPT_HTTPHEADER => [
"Content-Type: application/json",
"ff-secret-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://billing.funnelfox.com/{org_id}/v1/subscription/migration"
payload := strings.NewReader("{\n \"subs_id\": \"3c90c3cc-0d44-4b50-8888-8dd25736052a\",\n \"pp_ident\": \"<string>\",\n \"reason\": \"\",\n \"comment\": \"\",\n \"external_id\": \"<string>\",\n \"strategy\": \"price_prorate\",\n \"dry_run\": false,\n \"strict_mode\": true\n}")
req, _ := http.NewRequest("POST", url, payload)
req.Header.Add("ff-secret-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://billing.funnelfox.com/{org_id}/v1/subscription/migration")
.header("ff-secret-key", "<api-key>")
.header("Content-Type", "application/json")
.body("{\n \"subs_id\": \"3c90c3cc-0d44-4b50-8888-8dd25736052a\",\n \"pp_ident\": \"<string>\",\n \"reason\": \"\",\n \"comment\": \"\",\n \"external_id\": \"<string>\",\n \"strategy\": \"price_prorate\",\n \"dry_run\": false,\n \"strict_mode\": true\n}")
.asString();require 'uri'
require 'net/http'
url = URI("https://billing.funnelfox.com/{org_id}/v1/subscription/migration")
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
request = Net::HTTP::Post.new(url)
request["ff-secret-key"] = '<api-key>'
request["Content-Type"] = 'application/json'
request.body = "{\n \"subs_id\": \"3c90c3cc-0d44-4b50-8888-8dd25736052a\",\n \"pp_ident\": \"<string>\",\n \"reason\": \"\",\n \"comment\": \"\",\n \"external_id\": \"<string>\",\n \"strategy\": \"price_prorate\",\n \"dry_run\": false,\n \"strict_mode\": true\n}"
response = http.request(request)
puts response.read_body{
"migration_strategy": "price_prorate",
"payment_result": {
"checkout_status": "processing",
"failed_message_for_user": "<string>",
"order_id": "3c90c3cc-0d44-4b50-8888-8dd25736052a"
},
"charged_amount": 123,
"subs_id": "3c90c3cc-0d44-4b50-8888-8dd25736052a",
"oneoff_id": "3c90c3cc-0d44-4b50-8888-8dd25736052a"
}Subscription management
Migrate a subscription to a new plan
Switch an active subscription to a different Price Point (plan).
POST
/
subscription
/
migration
Migrate a subscription to a new plan
curl --request POST \
--url https://billing.funnelfox.com/{org_id}/v1/subscription/migration \
--header 'Content-Type: application/json' \
--header 'ff-secret-key: <api-key>' \
--data '
{
"subs_id": "3c90c3cc-0d44-4b50-8888-8dd25736052a",
"pp_ident": "<string>",
"reason": "",
"comment": "",
"external_id": "<string>",
"strategy": "price_prorate",
"dry_run": false,
"strict_mode": true
}
'import requests
url = "https://billing.funnelfox.com/{org_id}/v1/subscription/migration"
payload = {
"subs_id": "3c90c3cc-0d44-4b50-8888-8dd25736052a",
"pp_ident": "<string>",
"reason": "",
"comment": "",
"external_id": "<string>",
"strategy": "price_prorate",
"dry_run": False,
"strict_mode": True
}
headers = {
"ff-secret-key": "<api-key>",
"Content-Type": "application/json"
}
response = requests.post(url, json=payload, headers=headers)
print(response.text)const options = {
method: 'POST',
headers: {'ff-secret-key': '<api-key>', 'Content-Type': 'application/json'},
body: JSON.stringify({
subs_id: '3c90c3cc-0d44-4b50-8888-8dd25736052a',
pp_ident: '<string>',
reason: '',
comment: '',
external_id: '<string>',
strategy: 'price_prorate',
dry_run: false,
strict_mode: true
})
};
fetch('https://billing.funnelfox.com/{org_id}/v1/subscription/migration', 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://billing.funnelfox.com/{org_id}/v1/subscription/migration",
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([
'subs_id' => '3c90c3cc-0d44-4b50-8888-8dd25736052a',
'pp_ident' => '<string>',
'reason' => '',
'comment' => '',
'external_id' => '<string>',
'strategy' => 'price_prorate',
'dry_run' => false,
'strict_mode' => true
]),
CURLOPT_HTTPHEADER => [
"Content-Type: application/json",
"ff-secret-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://billing.funnelfox.com/{org_id}/v1/subscription/migration"
payload := strings.NewReader("{\n \"subs_id\": \"3c90c3cc-0d44-4b50-8888-8dd25736052a\",\n \"pp_ident\": \"<string>\",\n \"reason\": \"\",\n \"comment\": \"\",\n \"external_id\": \"<string>\",\n \"strategy\": \"price_prorate\",\n \"dry_run\": false,\n \"strict_mode\": true\n}")
req, _ := http.NewRequest("POST", url, payload)
req.Header.Add("ff-secret-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://billing.funnelfox.com/{org_id}/v1/subscription/migration")
.header("ff-secret-key", "<api-key>")
.header("Content-Type", "application/json")
.body("{\n \"subs_id\": \"3c90c3cc-0d44-4b50-8888-8dd25736052a\",\n \"pp_ident\": \"<string>\",\n \"reason\": \"\",\n \"comment\": \"\",\n \"external_id\": \"<string>\",\n \"strategy\": \"price_prorate\",\n \"dry_run\": false,\n \"strict_mode\": true\n}")
.asString();require 'uri'
require 'net/http'
url = URI("https://billing.funnelfox.com/{org_id}/v1/subscription/migration")
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
request = Net::HTTP::Post.new(url)
request["ff-secret-key"] = '<api-key>'
request["Content-Type"] = 'application/json'
request.body = "{\n \"subs_id\": \"3c90c3cc-0d44-4b50-8888-8dd25736052a\",\n \"pp_ident\": \"<string>\",\n \"reason\": \"\",\n \"comment\": \"\",\n \"external_id\": \"<string>\",\n \"strategy\": \"price_prorate\",\n \"dry_run\": false,\n \"strict_mode\": true\n}"
response = http.request(request)
puts response.read_body{
"migration_strategy": "price_prorate",
"payment_result": {
"checkout_status": "processing",
"failed_message_for_user": "<string>",
"order_id": "3c90c3cc-0d44-4b50-8888-8dd25736052a"
},
"charged_amount": 123,
"subs_id": "3c90c3cc-0d44-4b50-8888-8dd25736052a",
"oneoff_id": "3c90c3cc-0d44-4b50-8888-8dd25736052a"
}Supports two migration strategies:
- delayed_start - Schedules the new subscription to start when the customer’s already-paid time ends
- price_prorate - Unused time is credited toward the new plan’s first payment
migration_strategy field showing which strategy was actually applied.
Strict Mode
By default (strict_mode: true), if the requested strategy isn’t available, the API returns a 400 error. Set strict_mode: false to automatically switch to the alternative strategy when the selected one fails.
Additional Options
Usedry_run: true to preview charges without executing the migration. Supports migrations from subscription to subscription or subscription to lifetime purchases.Authorizations
Secret key for FunnelFox Payments API. Required for all requests.
Path Parameters
Organization ID
Body
application/json
Unique identifier of the price point
Maximum string length:
256Pattern:
^[-A-Za-z_0-9]{1,256}$Short reason code (e.g., "duplicate_payment", "unauthorized_charge")
Maximum string length:
256Human-readable comment or reference (e.g., support ticket link)
Maximum string length:
2048Your unique identifier for the user
Maximum string length:
256Available options:
price_prorate, delayed_start Was this page helpful?
