Manage Payment Method
curl --request POST \
--url https://api.nozle.app/engine/api/v1/customers/{customer_id}/payment-method/manage \
--header 'Authorization: Bearer <token>' \
--header 'Content-Type: application/json' \
--data '
{
"return_url": "<string>"
}
'import requests
url = "https://api.nozle.app/engine/api/v1/customers/{customer_id}/payment-method/manage"
payload = { "return_url": "<string>" }
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({return_url: '<string>'})
};
fetch('https://api.nozle.app/engine/api/v1/customers/{customer_id}/payment-method/manage', options)
.then(res => res.json())
.then(res => console.log(res))
.catch(err => console.error(err));package main
import (
"fmt"
"strings"
"net/http"
"io"
)
func main() {
url := "https://api.nozle.app/engine/api/v1/customers/{customer_id}/payment-method/manage"
payload := strings.NewReader("{\n \"return_url\": \"<string>\"\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))
}require 'uri'
require 'net/http'
url = URI("https://api.nozle.app/engine/api/v1/customers/{customer_id}/payment-method/manage")
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 \"return_url\": \"<string>\"\n}"
response = http.request(request)
puts response.read_body<?php
$curl = curl_init();
curl_setopt_array($curl, [
CURLOPT_URL => "https://api.nozle.app/engine/api/v1/customers/{customer_id}/payment-method/manage",
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([
'return_url' => '<string>'
]),
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;
}HttpResponse<String> response = Unirest.post("https://api.nozle.app/engine/api/v1/customers/{customer_id}/payment-method/manage")
.header("Authorization", "Bearer <token>")
.header("Content-Type", "application/json")
.body("{\n \"return_url\": \"<string>\"\n}")
.asString();{
"manage_payment_method.provider": "<string>",
"manage_payment_method.url": "<string>",
"manage_payment_method.expires_at": {}
}Checkout & Subscriptions
Manage Payment Method
POST
/
engine
/
api
/
v1
/
customers
/
{customer_id}
/
payment-method
/
manage
Manage Payment Method
curl --request POST \
--url https://api.nozle.app/engine/api/v1/customers/{customer_id}/payment-method/manage \
--header 'Authorization: Bearer <token>' \
--header 'Content-Type: application/json' \
--data '
{
"return_url": "<string>"
}
'import requests
url = "https://api.nozle.app/engine/api/v1/customers/{customer_id}/payment-method/manage"
payload = { "return_url": "<string>" }
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({return_url: '<string>'})
};
fetch('https://api.nozle.app/engine/api/v1/customers/{customer_id}/payment-method/manage', options)
.then(res => res.json())
.then(res => console.log(res))
.catch(err => console.error(err));package main
import (
"fmt"
"strings"
"net/http"
"io"
)
func main() {
url := "https://api.nozle.app/engine/api/v1/customers/{customer_id}/payment-method/manage"
payload := strings.NewReader("{\n \"return_url\": \"<string>\"\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))
}require 'uri'
require 'net/http'
url = URI("https://api.nozle.app/engine/api/v1/customers/{customer_id}/payment-method/manage")
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 \"return_url\": \"<string>\"\n}"
response = http.request(request)
puts response.read_body<?php
$curl = curl_init();
curl_setopt_array($curl, [
CURLOPT_URL => "https://api.nozle.app/engine/api/v1/customers/{customer_id}/payment-method/manage",
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([
'return_url' => '<string>'
]),
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;
}HttpResponse<String> response = Unirest.post("https://api.nozle.app/engine/api/v1/customers/{customer_id}/payment-method/manage")
.header("Authorization", "Bearer <token>")
.header("Content-Type", "application/json")
.body("{\n \"return_url\": \"<string>\"\n}")
.asString();{
"manage_payment_method.provider": "<string>",
"manage_payment_method.url": "<string>",
"manage_payment_method.expires_at": {}
}Base URL:
For
The idempotency option (
https://api.nozle.app/engine/api/v1.
Return the Stripe Billing Portal page where a customer adds or changes their payment method. Only Stripe customers have this page.
Auth: secret key (sk_) with payment:write, or trusted internal auth. Publishable keys are always refused with 403. A customer-scoped credential reaches only its own customer.
Nozle stores no card data and never shows card details. The payment method lives at the payment provider, so this endpoint sends the customer there. Call it from your backend for the logged-in user or team, then redirect the browser to the returned
url.Request
string
required
External customer ID. The customer must already exist and be linked to a payment provider.
string
Where Stripe sends the customer when they leave the Billing Portal. It must be an absolute HTTPS URL on an allowlisted origin, the same list as checkout. If omitted, Nozle uses the organization’s default return URL. The body can be empty.
{ "return_url": "https://app.example.com/settings/billing" }
Response
Core’s body is returned unchanged:string
required
Always
stripe.string
required
A Stripe Billing Portal URL. Redirect the customer to it.
string | null
When the page stops working, as an RFC 3339 UTC timestamp.
null for Stripe Billing Portal sessions.{
"manage_payment_method": {
"url": "https://billing.stripe.com/p/session/test_...",
"expires_at": null,
"provider": "stripe"
}
}
Razorpay customers
Razorpay customers have no payment method to manage here. They pay each invoice through a Razorpay payment link: when an invoice is finalized and no mandate charges it, Nozle emails the customer a payment request for that invoice with a Pay invoice button (the billing entity and the customer need an email address). The button always opens a current Razorpay payment link, also after an earlier link expired, and paying it marks the invoice paid. Dunning campaigns do not send a second request for such an invoice. For a Razorpay customer this endpoint answers422 razorpay_pays_by_link and creates nothing at Razorpay.
Errors
The request is checked in this order: valid JSON body,return_url, then Core.
| Status | Code | Cause |
|---|---|---|
400 | invalid_return_url | return_url is not an absolute HTTPS URL, or its origin is not allowlisted. |
403 | — | A publishable key was used. The Engine answers with an error message, not a code. |
404 | customer_not_found | Unknown customer. |
422 | no_linked_payment_provider | The customer has no payment provider. |
422 | razorpay_pays_by_link | The customer pays through Razorpay. Razorpay customers pay each invoice by payment link, so there is no payment method to manage. |
422 | provider_not_supported | The customer’s provider is not Stripe (Adyen, Cashfree, Flutterwave, GoCardless and Moneyhash are not supported). |
422 | missing_payment_provider_customer | The customer could not be created at Stripe. |
422 errors the code is in error_details, for example {"code": "validation_errors", "error_details": {"base": ["razorpay_pays_by_link"]}}. Match on the code, never on the message text.
SDKs
The SDK methods return the innermanage_payment_method object.
| SDK | Method |
|---|---|
| Node | client.managePaymentMethod(customerId, returnUrl?, { idempotencyKey? }) |
| Python | manage_payment_method(customer_id, return_url=None, *, idempotency_key=None) |
| Go | ManagePaymentMethod(ctx, customerID, returnURL, key) |
idempotencyKey, idempotency_key, or key in Go) is optional. Send the same value when retrying the same request.
See the Billing pages: Node, Python and Go.