Import External Billing State
curl --request POST \
--url https://api.nozle.app/api/v1/customers/{customer_id}/billing-state-imports \
--header 'Authorization: Bearer <token>'import requests
url = "https://api.nozle.app/api/v1/customers/{customer_id}/billing-state-imports"
headers = {"Authorization": "Bearer <token>"}
response = requests.post(url, headers=headers)
print(response.text)const options = {method: 'POST', headers: {Authorization: 'Bearer <token>'}};
fetch('https://api.nozle.app/api/v1/customers/{customer_id}/billing-state-imports', options)
.then(res => res.json())
.then(res => console.log(res))
.catch(err => console.error(err));package main
import (
"fmt"
"net/http"
"io"
)
func main() {
url := "https://api.nozle.app/api/v1/customers/{customer_id}/billing-state-imports"
req, _ := http.NewRequest("POST", url, nil)
req.Header.Add("Authorization", "Bearer <token>")
res, _ := http.DefaultClient.Do(req)
defer res.Body.Close()
body, _ := io.ReadAll(res.Body)
fmt.Println(string(body))
}require 'uri'
require 'net/http'
url = URI("https://api.nozle.app/api/v1/customers/{customer_id}/billing-state-imports")
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
request = Net::HTTP::Post.new(url)
request["Authorization"] = 'Bearer <token>'
response = http.request(request)
puts response.read_body<?php
$curl = curl_init();
curl_setopt_array($curl, [
CURLOPT_URL => "https://api.nozle.app/api/v1/customers/{customer_id}/billing-state-imports",
CURLOPT_RETURNTRANSFER => true,
CURLOPT_ENCODING => "",
CURLOPT_MAXREDIRS => 10,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
CURLOPT_CUSTOMREQUEST => "POST",
CURLOPT_HTTPHEADER => [
"Authorization: Bearer <token>"
],
]);
$response = curl_exec($curl);
$err = curl_error($curl);
curl_close($curl);
if ($err) {
echo "cURL Error #:" . $err;
} else {
echo $response;
}HttpResponse<String> response = Unirest.post("https://api.nozle.app/api/v1/customers/{customer_id}/billing-state-imports")
.header("Authorization", "Bearer <token>")
.asString();Checkout & Subscriptions
Import External Billing State
Import an existing paid subscription without creating a Nozle invoice or payment
POST
/
customers
/
{customer_id}
/
billing-state-imports
Import External Billing State
curl --request POST \
--url https://api.nozle.app/api/v1/customers/{customer_id}/billing-state-imports \
--header 'Authorization: Bearer <token>'import requests
url = "https://api.nozle.app/api/v1/customers/{customer_id}/billing-state-imports"
headers = {"Authorization": "Bearer <token>"}
response = requests.post(url, headers=headers)
print(response.text)const options = {method: 'POST', headers: {Authorization: 'Bearer <token>'}};
fetch('https://api.nozle.app/api/v1/customers/{customer_id}/billing-state-imports', options)
.then(res => res.json())
.then(res => console.log(res))
.catch(err => console.error(err));package main
import (
"fmt"
"net/http"
"io"
)
func main() {
url := "https://api.nozle.app/api/v1/customers/{customer_id}/billing-state-imports"
req, _ := http.NewRequest("POST", url, nil)
req.Header.Add("Authorization", "Bearer <token>")
res, _ := http.DefaultClient.Do(req)
defer res.Body.Close()
body, _ := io.ReadAll(res.Body)
fmt.Println(string(body))
}require 'uri'
require 'net/http'
url = URI("https://api.nozle.app/api/v1/customers/{customer_id}/billing-state-imports")
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
request = Net::HTTP::Post.new(url)
request["Authorization"] = 'Bearer <token>'
response = http.request(request)
puts response.read_body<?php
$curl = curl_init();
curl_setopt_array($curl, [
CURLOPT_URL => "https://api.nozle.app/api/v1/customers/{customer_id}/billing-state-imports",
CURLOPT_RETURNTRANSFER => true,
CURLOPT_ENCODING => "",
CURLOPT_MAXREDIRS => 10,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
CURLOPT_CUSTOMREQUEST => "POST",
CURLOPT_HTTPHEADER => [
"Authorization: Bearer <token>"
],
]);
$response = curl_exec($curl);
$err = curl_error($curl);
curl_close($curl);
if ($err) {
echo "cURL Error #:" . $err;
} else {
echo $response;
}HttpResponse<String> response = Unirest.post("https://api.nozle.app/api/v1/customers/{customer_id}/billing-state-imports")
.header("Authorization", "Bearer <token>")
.asString();Use this Core API when another system already bills the customer and Nozle should provide the plan entitlements without charging them again.
Base URL:
For an Entity subscription, add
The referenced customer, plan, and optional Entity must already exist in the authenticated organization. The period must contain the current time and span exactly one plan interval. Periods use exact timestamp-based, half-open boundaries:
Nozle creates no invoice, checkout, payment-provider customer, or payment request. Externally billed subscriptions are excluded from Nozle invoicing, upgrades, cancellation, dunning, and payment collection.
If
Next: Import the opening credit balance.
https://core.nozle.app/api/v1
Auth: secret key with subscription:write when API permissions are enabled. Publishable keys are rejected. Idempotency-Key is required.
Import the subscription
POST /customers/workspace_123/billing-state-imports
Authorization: Bearer sk_nozle_...
Idempotency-Key: workspace-123-pro-cutover-v1
Content-Type: application/json
{
"billing_state_import": {
"dry_run": false,
"subscription": {
"plan_code": "pro_monthly",
"status": "active",
"billing_authority": "external",
"external_reference": "merchant_subscription_123",
"current_period_start": "2026-08-15T00:00:00Z",
"current_period_end": "2026-09-15T00:00:00Z"
}
}
}
external_entity_id beside subscription:
{
"billing_state_import": {
"external_entity_id": "user_42",
"subscription": {
"plan_code": "pro_monthly",
"status": "active",
"billing_authority": "external",
"external_reference": "merchant_user_42_pro",
"current_period_start": "2026-08-15T00:00:00Z",
"current_period_end": "2026-09-15T00:00:00Z"
}
}
}
current_period_start is inclusive and current_period_end is the first instant of the next period. Non-midnight payment-provider boundaries are preserved exactly—for example, a monthly period from 2026-08-15T10:37:42Z must end at 2026-09-15T10:37:42Z. Weekly, monthly, quarterly, semiannual, and yearly calendar advancement is supported, including month-end and leap-year clamping. Shorter, longer, or otherwise mismatched periods are rejected. The imported boundaries are authoritative for current usage reporting and opening-balance expiry. Independent credit-grant cadences remain independent—for example, monthly credits on an annual plan still reset monthly—and lifetime grants remain non-expiring.
Response
{
"billing_state_import": {
"id": "import_uuid",
"status": "completed",
"dry_run": false,
"external_customer_id": "workspace_123",
"subscription": {
"id": "subscription_uuid",
"external_subscription_id": "merchant_subscription_123",
"external_reference": "merchant_subscription_123",
"plan_code": "pro_monthly",
"status": "active",
"billing_authority": "external",
"credit_import_required": true,
"current_period_start": "2026-08-15T00:00:00Z",
"current_period_end": "2026-09-15T00:00:00Z",
"invoice_created": false
},
"replayed": false
}
}
credit_import_required is true, import every opening Credit System balance before switching usage authority to Nozle. Scheduled plan grants remain paused until that import is complete.
For an Entity import, save the external_subscription_id returned in this response. Nozle generates that identifier for the Entity subscription; external_reference remains your source-system audit reference and is not used as the Entity subscription ID.
Dry run and retries
Setdry_run to true to run the same idempotency, Entity ownership, subscription-slot, grant-scope, and period checks as execution without writing subscription state.
Retry an uncertain response with the same idempotency key and identical payload. A replay returns the original result. Reusing the key with a different request is rejected.
This endpoint does not transfer billing authority to Nozle. Adopting payment collection later requires a separate explicit migration flow.