Import
Commit a Stripe Import
Import a Stripe export — customers, plans, and subscriptions — idempotently.
POST
/
v1
/
import
/
stripe
/
commit
Import a Stripe export (customers, plans, subscriptions)
curl --request POST \
--url https://billing.example.com/v1/import/stripe/commit \
--header 'Authorization: Bearer <token>' \
--header 'Content-Type: application/json' \
--data '
{
"customers": [
{}
],
"products": [
{}
],
"prices": [
{}
],
"subscriptions": [
{}
],
"payment_methods": [
{}
]
}
'import requests
url = "https://billing.example.com/v1/import/stripe/commit"
payload = {
"customers": [{}],
"products": [{}],
"prices": [{}],
"subscriptions": [{}],
"payment_methods": [{}]
}
headers = {
"Authorization": "Bearer <token>",
"Content-Type": "application/json"
}
response = requests.post(url, json=payload, headers=headers)
print(response.text)const options = {
method: 'POST',
headers: {Authorization: 'Bearer <token>', 'Content-Type': 'application/json'},
body: JSON.stringify({
customers: [{}],
products: [{}],
prices: [{}],
subscriptions: [{}],
payment_methods: [{}]
})
};
fetch('https://billing.example.com/v1/import/stripe/commit', 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.example.com/v1/import/stripe/commit",
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([
'customers' => [
[
]
],
'products' => [
[
]
],
'prices' => [
[
]
],
'subscriptions' => [
[
]
],
'payment_methods' => [
[
]
]
]),
CURLOPT_HTTPHEADER => [
"Authorization: Bearer <token>",
"Content-Type: application/json"
],
]);
$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.example.com/v1/import/stripe/commit"
payload := strings.NewReader("{\n \"customers\": [\n {}\n ],\n \"products\": [\n {}\n ],\n \"prices\": [\n {}\n ],\n \"subscriptions\": [\n {}\n ],\n \"payment_methods\": [\n {}\n ]\n}")
req, _ := http.NewRequest("POST", url, payload)
req.Header.Add("Authorization", "Bearer <token>")
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.example.com/v1/import/stripe/commit")
.header("Authorization", "Bearer <token>")
.header("Content-Type", "application/json")
.body("{\n \"customers\": [\n {}\n ],\n \"products\": [\n {}\n ],\n \"prices\": [\n {}\n ],\n \"subscriptions\": [\n {}\n ],\n \"payment_methods\": [\n {}\n ]\n}")
.asString();require 'uri'
require 'net/http'
url = URI("https://billing.example.com/v1/import/stripe/commit")
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
request = Net::HTTP::Post.new(url)
request["Authorization"] = 'Bearer <token>'
request["Content-Type"] = 'application/json'
request.body = "{\n \"customers\": [\n {}\n ],\n \"products\": [\n {}\n ],\n \"prices\": [\n {}\n ],\n \"subscriptions\": [\n {}\n ],\n \"payment_methods\": [\n {}\n ]\n}"
response = http.request(request)
puts response.read_body{
"plan": {},
"created": {},
"failures": [
{
"kind": "<string>",
"stripe_id": "<string>",
"error": "<string>"
}
]
}{
"error": {
"code": "validation_failed",
"message": "<string>"
}
}{
"error": {
"code": "validation_failed",
"message": "<string>"
}
}Takes the same payload as preview and
performs the import. Re-running is safe: already-imported Stripe ids and
records that already exist (by email or plan code) are skipped.
Subscriptions are imported in their current billing state via a direct
insert — no invoice, charge, or ledger entry is generated — so Recurso
takes over at the next renewal instead of re-billing the current cycle.
Card payment methods are NOT imported: card data can’t be migrated from a
static export.
The response contains the executed
Example request
curl -X POST https://api.recurso.dev/v1/import/stripe/commit \
-H "Authorization: Bearer $API_KEY" \
-H "Content-Type: application/json" \
-d @stripe-export.json
plan, a created map of per-kind
counts, and a failures array (kind, stripe_id, error) — per-object
failures are reported there rather than aborting the whole import.
After committing, prove the migration with the
compare gate before pointing
billing at Recurso.Authorizations
Tenant API key obtained from POST /auth/register or POST /v1/developer/keys.
Body
application/json
The Stripe export JSON (same shape as the preview endpoint).
⌘I
Import a Stripe export (customers, plans, subscriptions)
curl --request POST \
--url https://billing.example.com/v1/import/stripe/commit \
--header 'Authorization: Bearer <token>' \
--header 'Content-Type: application/json' \
--data '
{
"customers": [
{}
],
"products": [
{}
],
"prices": [
{}
],
"subscriptions": [
{}
],
"payment_methods": [
{}
]
}
'import requests
url = "https://billing.example.com/v1/import/stripe/commit"
payload = {
"customers": [{}],
"products": [{}],
"prices": [{}],
"subscriptions": [{}],
"payment_methods": [{}]
}
headers = {
"Authorization": "Bearer <token>",
"Content-Type": "application/json"
}
response = requests.post(url, json=payload, headers=headers)
print(response.text)const options = {
method: 'POST',
headers: {Authorization: 'Bearer <token>', 'Content-Type': 'application/json'},
body: JSON.stringify({
customers: [{}],
products: [{}],
prices: [{}],
subscriptions: [{}],
payment_methods: [{}]
})
};
fetch('https://billing.example.com/v1/import/stripe/commit', 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.example.com/v1/import/stripe/commit",
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([
'customers' => [
[
]
],
'products' => [
[
]
],
'prices' => [
[
]
],
'subscriptions' => [
[
]
],
'payment_methods' => [
[
]
]
]),
CURLOPT_HTTPHEADER => [
"Authorization: Bearer <token>",
"Content-Type: application/json"
],
]);
$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.example.com/v1/import/stripe/commit"
payload := strings.NewReader("{\n \"customers\": [\n {}\n ],\n \"products\": [\n {}\n ],\n \"prices\": [\n {}\n ],\n \"subscriptions\": [\n {}\n ],\n \"payment_methods\": [\n {}\n ]\n}")
req, _ := http.NewRequest("POST", url, payload)
req.Header.Add("Authorization", "Bearer <token>")
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.example.com/v1/import/stripe/commit")
.header("Authorization", "Bearer <token>")
.header("Content-Type", "application/json")
.body("{\n \"customers\": [\n {}\n ],\n \"products\": [\n {}\n ],\n \"prices\": [\n {}\n ],\n \"subscriptions\": [\n {}\n ],\n \"payment_methods\": [\n {}\n ]\n}")
.asString();require 'uri'
require 'net/http'
url = URI("https://billing.example.com/v1/import/stripe/commit")
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
request = Net::HTTP::Post.new(url)
request["Authorization"] = 'Bearer <token>'
request["Content-Type"] = 'application/json'
request.body = "{\n \"customers\": [\n {}\n ],\n \"products\": [\n {}\n ],\n \"prices\": [\n {}\n ],\n \"subscriptions\": [\n {}\n ],\n \"payment_methods\": [\n {}\n ]\n}"
response = http.request(request)
puts response.read_body{
"plan": {},
"created": {},
"failures": [
{
"kind": "<string>",
"stripe_id": "<string>",
"error": "<string>"
}
]
}{
"error": {
"code": "validation_failed",
"message": "<string>"
}
}{
"error": {
"code": "validation_failed",
"message": "<string>"
}
}