API REFERENCE v1

DEVELOPERS

Logs

The Log entry object

The unified log: API requests, webhooks and decisions.

ATTRIBUTES

  • object enum
    • log_entry
  • id string
  • livemode boolean
  • occurred_at string date-time

    Millisecond precision.

  • source enum

    stripe (webhooks), api (your calls with an API key), nochurn (decisions), widget, webhook (outgoing), a billing tool (chargebee, zuora…: its webhooks and what NoChurn reads from it), a support tool (intercom, crisp, zendesk, hubspot, helpscout, gorgias).

    • stripe
    • api
    • nochurn
    • widget
    • webhook
    • chargebee
    • recurly
    • maxio
    • zuora
    • paddle
    • lemon_squeezy
    • polar
    • braintree
    • mollie
    • gocardless
    • razorpay
    • app_store
    • google_play
    • custom
    • intercom
    • crisp
    • zendesk
    • hubspot
    • helpscout
    • gorgias
  • type string
  • customer object | null

    The customer this line is about (open it with /v1/customers/{id}).

    Show child attributesHide child attributes
  • summary string | null
Show 5 more attributesHide 5 attributes
  • status string

    HTTP status ("200", "500") or ok, received, ignored, failed.

  • error boolean
  • why string | null

    Why this happened (the reason of the NoChurn decision).

  • ref object | null
    Show child attributesHide child attributes
  • request_id string | null

Schema: LogEntry

THE LOG ENTRY OBJECT
{
  "object": "log_entry",
  "id": "log_FtFYHxFSjx7e96PG",
  "livemode": false,
  "occurred_at": "2026-11-04T09:59:00.000Z",
  "source": "api",
  "type": "POST /v1/customers",
  "customer": null,
  "summary": null,
  "status": "500",
  "error": true,
  "why": null,
  "ref": null,
  "request_id": null
}

Retrieve a line with its payload

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

PATH PARAMETERS

  • id string REQUIRED

RETURNS

ERRORS

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

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

uri = URI("https://api.nochurn.sh/v1/logs/log_6NzztFYECRW8iXtw")
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) }
log_entry = JSON.parse(res.body)
RESPONSE 200
{
  "object": "log_entry",
  "id": "log_6NzztFYECRW8iXtw",
  "livemode": false,
  "occurred_at": "2026-11-04T10:00:00.000Z",
  "source": "nochurn",
  "type": "x.y",
  "customer": null,
  "summary": null,
  "status": "ok",
  "error": false,
  "why": null,
  "ref": null,
  "request_id": null,
  "payload": {
    "id": "in_1",
    "client_secret": "EXAMPLE000"
  }
}

List the unified log, newest first

Stripe webhooks, your API calls, NoChurn decisions (and later the widget, outgoing webhooks and support tools), kept 90 days. Live view: poll with after=<poll_cursor> every few seconds.

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

QUERY PARAMETERS

  • source string

    Comma-separated: stripe, api, nochurn, widget, webhook, chargebee, recurly, maxio, zuora, paddle, lemon_squeezy, polar, braintree, mollie, gocardless, razorpay, app_store, google_play, custom, intercom, crisp, zendesk, hubspot, helpscout, gorgias; billing stands for every billing tool (stripe, chargebee, recurly, maxio, zuora, paddle, lemon_squeezy, polar, braintree, mollie, gocardless, razorpay, app_store, google_play, custom).

  • errors boolean

    Errors only.

  • q string

    A customer (cst_, cus_, your id, email), an id (log_, nevt_, iwh_) or text.

  • customer string

    cst_…, cus_… or your id.

  • since string date-time

    Default: 24 hours before until. The window is 31 days at most.

  • until string date-time
  • limit integer default 20 · 1 to 100
  • Older lines.

  • after string

    Only newer lines (polling).

RETURNS

ERRORS

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

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

uri = URI("https://api.nochurn.sh/v1/logs?source=billing,api")
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": [
    {
      "object": "log_entry",
      "id": "log_FtFYHxFSjx7e96PG",
      "livemode": false,
      "occurred_at": "2026-11-04T09:59:00.000Z",
      "source": "api",
      "type": "POST /v1/customers",
      "customer": null,
      "summary": null,
      "status": "500",
      "error": true,
      "why": null,
      "ref": null,
      "request_id": null
    },
    {
      "object": "log_entry",
      "id": "log_6DtqCEhQ718CVQtU",
      "livemode": false,
      "occurred_at": "2026-11-04T09:58:00.000Z",
      "source": "stripe",
      "type": "invoice.payment_failed",
      "customer": {
        "id": "cst_TbLNas4T3Y6wWvgN",
        "name": null,
        "email": "customer1@acme-corp.dev"
      },
      "summary": "$49.00 · insufficient_funds",
      "status": "received",
      "error": false,
      "why": null,
      "ref": null,
      "request_id": null
    }
  ],
  "has_more": false,
  "next_cursor": null,
  "poll_cursor": "log_FtFYHxFSjx7e96PG",
  "retention_days": 90
}

Count of today's lines and of the errors of a window

today_count: today in the account's time zone, all lines. errors_count: the error lines of the window and filters passed (same params as GET /v1/logs, 24 hours by default).

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

QUERY PARAMETERS

  • source string

    Comma-separated sources, or billing (every billing tool), as GET /v1/logs.

  • errors boolean

    Accepted for symmetry with GET /v1/logs: errors_count only counts errors anyway.

  • q string
  • customer string
  • since string date-time

    Default: 24 hours before until. The window is 31 days at most.

  • until string date-time

RETURNS

  • 200 errors of one source over 7 days — a LogStats

ERRORS

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

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

uri = URI("https://api.nochurn.sh/v1/logs/stats")
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) }
log_stats = JSON.parse(res.body)
RESPONSE 200
{
  "object": "log_stats",
  "livemode": false,
  "timezone": "Europe/Paris",
  "today_count": 4,
  "errors_count": 3,
  "retention_days": 90
}