API REFERENCE v1

CORE RESOURCES

Billing

The Payment object

Subscriptions and payments sent by your own billing, when you don't use a connected billing tool.

ATTRIBUTES

Show 7 more attributesHide 7 attributes

Schema: Payment

THE PAYMENT OBJECT
{
  "object": "payment",
  "id": "npay_TBSmR2L4qghkw8q1",
  "livemode": false,
  "customer": "cst_xWuFumaBvkQ9HWYw",
  "subscription": "nsub_Zv2tbZpFo5YxCaPy",
  "invoice": "ninv_JKntY71SKsXEe55e",
  "external_id": "pay_9",
  "stripe_charge_id": null,
  "status": "failed",
  "amount_cents": 4900,
  "currency": "eur",
  "decline_code": "do_not_honor",
  "failure_code": null,
  "occurred_at": "2026-11-04T10:00:00Z",
  "created_at": "2026-11-04T10:00:00Z"
}

Send a payment (billing by API)

status failed opens recovery: NoChurn plans retries and requests each one with billing.retry_requested. status succeeded pays the invoice and ends recovery. Idempotent on your external_id.

  • Secret key (nc_test_… or nc_live_…). Restricted keys need write access.
  • AI apps (OAuth): sandbox scope, test mode only.

BODY PARAMETERS

RETURNS

POST/v1/payments
curl https://api.nochurn.sh/v1/payments \
  -H "Authorization: Bearer nc_test_…" \
  -H "Content-Type: application/json" \
  -d '{
    "external_id": "pay_9",
    "customer": "user_42",
    "subscription": "sub_42",
    "invoice": "inv_1",
    "amount": 4900,
    "currency": "eur",
    "status": "failed",
    "decline_code": "do_not_honor"
  }'
const res = await fetch("https://api.nochurn.sh/v1/payments", {
  method: "POST",
  headers: {
    Authorization: "Bearer nc_test_…",
    "Content-Type": "application/json",
  },
  body: JSON.stringify({
    "external_id": "pay_9",
    "customer": "user_42",
    "subscription": "sub_42",
    "invoice": "inv_1",
    "amount": 4900,
    "currency": "eur",
    "status": "failed",
    "decline_code": "do_not_honor"
  }),
});
const payment = await res.json();
import requests

res = requests.post(
    "https://api.nochurn.sh/v1/payments",
    headers={
        "Authorization": "Bearer nc_test_…",
    },
    json={
      "external_id": "pay_9",
      "customer": "user_42",
      "subscription": "sub_42",
      "invoice": "inv_1",
      "amount": 4900,
      "currency": "eur",
      "status": "failed",
      "decline_code": "do_not_honor"
    },
)
payment = res.json()
require "net/http"
require "json"

uri = URI("https://api.nochurn.sh/v1/payments")
req = Net::HTTP::Post.new(uri)
req["Authorization"] = "Bearer nc_test_…"
req["Content-Type"] = "application/json"
req.body = {
  "external_id" => "pay_9",
  "customer" => "user_42",
  "subscription" => "sub_42",
  "invoice" => "inv_1",
  "amount" => 4900,
  "currency" => "eur",
  "status" => "failed",
  "decline_code" => "do_not_honor"
}.to_json

res = Net::HTTP.start(uri.host, uri.port, use_ssl: true) { |http| http.request(req) }
payment = JSON.parse(res.body)
RESPONSE 201
{
  "object": "payment",
  "id": "npay_TBSmR2L4qghkw8q1",
  "livemode": false,
  "customer": "cst_xWuFumaBvkQ9HWYw",
  "subscription": "nsub_Zv2tbZpFo5YxCaPy",
  "invoice": "ninv_JKntY71SKsXEe55e",
  "external_id": "pay_9",
  "stripe_charge_id": null,
  "status": "failed",
  "amount_cents": 4900,
  "currency": "eur",
  "decline_code": "do_not_honor",
  "failure_code": null,
  "occurred_at": "2026-11-04T10:00:00Z",
  "created_at": "2026-11-04T10:00:00Z"
}

Payments (Stripe and API)

  • Secret key (nc_test_… or nc_live_…). Restricted keys need read access.
  • AI apps (OAuth): read scope.

QUERY PARAMETERS

RETURNS

GET/v1/payments
curl https://api.nochurn.sh/v1/payments \
  -H "Authorization: Bearer nc_test_…"
const res = await fetch("https://api.nochurn.sh/v1/payments", {
  headers: {
    Authorization: "Bearer nc_test_…",
  },
});
const page = await res.json();
import requests

res = requests.get(
    "https://api.nochurn.sh/v1/payments",
    headers={
        "Authorization": "Bearer nc_test_…",
    },
)
page = res.json()
require "net/http"
require "json"

uri = URI("https://api.nochurn.sh/v1/payments")
req = Net::HTTP::Get.new(uri)
req["Authorization"] = "Bearer nc_test_…"

res = Net::HTTP.start(uri.host, uri.port, use_ssl: true) { |http| http.request(req) }
page = JSON.parse(res.body)
RESPONSE 200
{
  "object": "list",
  "data": [],
  "has_more": false,
  "next_cursor": null
}

Send a subscription (billing by API)

Idempotent on your external_id: posting it again updates the subscription.

  • Secret key (nc_test_… or nc_live_…). Restricted keys need write access.
  • AI apps (OAuth): sandbox scope, test mode only.

BODY PARAMETERS

RETURNS

ERRORS

  • 409 conflict the account's billing comes from Stripe
POST/v1/subscriptions
curl https://api.nochurn.sh/v1/subscriptions \
  -H "Authorization: Bearer nc_test_…" \
  -H "Content-Type: application/json" \
  -d '{
    "external_id": "sub_42",
    "customer": {
      "external_id": "user_42",
      "email": "lena@globex.io"
    },
    "plan": "Pro",
    "amount": 4900,
    "currency": "eur",
    "interval": "month"
  }'
const res = await fetch("https://api.nochurn.sh/v1/subscriptions", {
  method: "POST",
  headers: {
    Authorization: "Bearer nc_test_…",
    "Content-Type": "application/json",
  },
  body: JSON.stringify({
    "external_id": "sub_42",
    "customer": {
      "external_id": "user_42",
      "email": "lena@globex.io"
    },
    "plan": "Pro",
    "amount": 4900,
    "currency": "eur",
    "interval": "month"
  }),
});
const subscription = await res.json();
import requests

res = requests.post(
    "https://api.nochurn.sh/v1/subscriptions",
    headers={
        "Authorization": "Bearer nc_test_…",
    },
    json={
      "external_id": "sub_42",
      "customer": {
        "external_id": "user_42",
        "email": "lena@globex.io"
      },
      "plan": "Pro",
      "amount": 4900,
      "currency": "eur",
      "interval": "month"
    },
)
subscription = res.json()
require "net/http"
require "json"

uri = URI("https://api.nochurn.sh/v1/subscriptions")
req = Net::HTTP::Post.new(uri)
req["Authorization"] = "Bearer nc_test_…"
req["Content-Type"] = "application/json"
req.body = {
  "external_id" => "sub_42",
  "customer" => {
    "external_id" => "user_42",
    "email" => "lena@globex.io"
  },
  "plan" => "Pro",
  "amount" => 4900,
  "currency" => "eur",
  "interval" => "month"
}.to_json

res = Net::HTTP.start(uri.host, uri.port, use_ssl: true) { |http| http.request(req) }
subscription = JSON.parse(res.body)
RESPONSE 201
{
  "object": "subscription",
  "id": "nsub_hd7rffvqSB3fF8LR",
  "customer": "cst_XBTW4dWsGxyLYcgT",
  "stripe_subscription_id": null,
  "external_id": "sub_42",
  "livemode": false,
  "status": "active",
  "plan": "Pro",
  "amount_cents": 4900,
  "currency": "eur",
  "interval": "month",
  "interval_count": 1,
  "mrr_cents": 4900,
  "current_period_end": null,
  "cancel_at_period_end": false,
  "canceled_at": null,
  "paused_until": null,
  "trial_end": null,
  "created_at": "2026-11-04T10:00:00.000Z"
}

Change a subscription sent by API (subscription.cancelled)

  • Secret key (nc_test_… or nc_live_…). Restricted keys need write access.
  • AI apps (OAuth): sandbox scope, test mode only.

PATH PARAMETERS

  • id string REQUIRED

    nsub_… or your external_id

BODY PARAMETERS

RETURNS

PATCH/v1/subscriptions/{id}
curl https://api.nochurn.sh/v1/subscriptions/nsub_Wf9KF5qNWWFiJ6N6 \
  -X PATCH \
  -H "Authorization: Bearer nc_test_…" \
  -H "Content-Type: application/json" \
  -d '{
    "status": "canceled"
  }'
const res = await fetch("https://api.nochurn.sh/v1/subscriptions/nsub_Wf9KF5qNWWFiJ6N6", {
  method: "PATCH",
  headers: {
    Authorization: "Bearer nc_test_…",
    "Content-Type": "application/json",
  },
  body: JSON.stringify({
    "status": "canceled"
  }),
});
const subscription = await res.json();
import requests

res = requests.patch(
    "https://api.nochurn.sh/v1/subscriptions/nsub_Wf9KF5qNWWFiJ6N6",
    headers={
        "Authorization": "Bearer nc_test_…",
    },
    json={
      "status": "canceled"
    },
)
subscription = res.json()
require "net/http"
require "json"

uri = URI("https://api.nochurn.sh/v1/subscriptions/nsub_Wf9KF5qNWWFiJ6N6")
req = Net::HTTP::Patch.new(uri)
req["Authorization"] = "Bearer nc_test_…"
req["Content-Type"] = "application/json"
req.body = {
  "status" => "canceled"
}.to_json

res = Net::HTTP.start(uri.host, uri.port, use_ssl: true) { |http| http.request(req) }
subscription = JSON.parse(res.body)
RESPONSE 200
{
  "object": "subscription",
  "id": "nsub_Wf9KF5qNWWFiJ6N6",
  "customer": "cst_5SJ8JmubQCqFJzQw",
  "stripe_subscription_id": null,
  "external_id": "sub_42",
  "livemode": false,
  "status": "canceled",
  "plan": null,
  "amount_cents": 4900,
  "currency": "eur",
  "interval": "month",
  "interval_count": 1,
  "mrr_cents": 0,
  "current_period_end": null,
  "cancel_at_period_end": false,
  "canceled_at": "2026-11-04T10:00:00.000Z",
  "paused_until": null,
  "trial_end": null,
  "created_at": "2026-11-04T10:00:00.000Z"
}