API Reference
Billing API

Billing API

The Billing API allows you to create Stripe checkout sessions, open the customer billing portal, and receive lifecycle events from Stripe. All billing actions are scoped to your tenant.

Billing endpoints are prefixed with /api/v1/billing. The webhook endpoint does not require an API key — it is authenticated by Stripe's signature header instead.

Authentication

All billing endpoints except POST /api/v1/billing/webhook require your API key:

X-API-Key: nat_pk_your_api_key_here

Plans overview

PlanScans / monthFeaturesPrice
Free50Functional testing$0
Pro500Functional + security scanning$49/mo
Team2,000All features + webhooks + priority support$149/mo
EnterpriseUnlimitedAll features + SSO/SAML + dedicated supportCustom
⚠️

When you exceed your monthly scan quota, subsequent scan requests return 402 Payment Required with the error code QUOTA_EXCEEDED. Upgrade your plan or wait for the quota reset date.


Endpoints

Create checkout session

Creates a Stripe-hosted checkout session for upgrading to a paid plan. The response contains a URL to redirect the user to.

POST /api/v1/billing/checkout

Request body:

FieldTypeRequiredDescription
price_idstringStripe Price ID (STRIPE_PRICE_ID_PRO or STRIPE_PRICE_ID_TEAM)
success_urlstringURL to redirect after successful payment
cancel_urlstringURL to redirect if checkout is cancelled
curl -X POST https://api.nat-testing.io/api/v1/billing/checkout \
  -H "X-API-Key: $NAT_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "price_id": "price_pro_monthly",
    "success_url": "https://app.nat-testing.io/dashboard?checkout=success",
    "cancel_url": "https://app.nat-testing.io/pricing"
  }'

Response 200 OK:

{
  "checkout_url": "https://checkout.stripe.com/c/pay/cs_test_..."
}

Error responses:

StatusCodeDescription
401UNAUTHORIZEDMissing or invalid API key
400INVALID_PRICE_IDThe provided price_id does not exist

Open billing portal

Creates a Stripe customer portal session so the tenant can manage their subscription, update payment methods, download invoices, or cancel their plan.

POST /api/v1/billing/portal

Request body:

FieldTypeRequiredDescription
return_urlstringURL to return to after leaving the portal
curl -X POST https://api.nat-testing.io/api/v1/billing/portal \
  -H "X-API-Key: $NAT_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "return_url": "https://app.nat-testing.io/dashboard"
  }'

Response 200 OK:

{
  "portal_url": "https://billing.stripe.com/p/session/..."
}

Get billing status

Returns the current billing plan and subscription status for your tenant.

GET /api/v1/billing/status
curl https://api.nat-testing.io/api/v1/billing/status \
  -H "X-API-Key: $NAT_API_KEY"

Response 200 OK:

{
  "plan": "pro",
  "subscription_status": "active",
  "current_period_end": "2025-02-01T00:00:00Z",
  "cancel_at_period_end": false,
  "stripe_customer_id": "cus_abc123xyz"
}

Response fields:

FieldTypeDescription
planstringCurrent plan: free, pro, team, enterprise
subscription_statusstringStripe subscription status: active, trialing, past_due, canceled
current_period_endstringISO 8601 timestamp when the current billing period ends
cancel_at_period_endbooleanWhether the subscription cancels at the end of the current period
stripe_customer_idstringStripe customer ID (useful for support requests)

Error responses:

StatusCodeDescription
401UNAUTHORIZEDMissing or invalid API key

Stripe webhook receiver

This endpoint receives lifecycle events from Stripe. It is called automatically by Stripe — you do not need to call it directly.

POST /api/v1/billing/webhook
⚠️

This endpoint does not use the X-API-Key header. It is authenticated using the Stripe-Signature header and the STRIPE_WEBHOOK_SECRET environment variable.

Handled event types:

Stripe eventNAT action
checkout.session.completedCreates or updates tenant record, issues API key
customer.subscription.updatedUpdates tenant plan tier and quota limits
customer.subscription.deletedDowngrades tenant to Free plan
invoice.payment_failedSends payment failure notification to tenant
invoice.paidSubscription renewed; quota reset for the new period
charge.refundedRefund processed; plan downgraded to Free if applicable

Environment variables required:

VariableDescription
STRIPE_SECRET_KEYStripe secret API key
STRIPE_WEBHOOK_SECRETStripe webhook signing secret (from Stripe dashboard)
STRIPE_PRICE_ID_PROStripe Price ID for the Pro plan
STRIPE_PRICE_ID_TEAMStripe Price ID for the Team plan

Sync tenant

Syncs a tenant record from the nat-app frontend into the NAT engine. This is called internally by nat-app after a user signs up or completes Stripe checkout, and is not typically called directly by end users.

POST /api/v1/billing/sync-tenant

This endpoint is used by the nat-app frontend to provision or update a tenant in the engine after signup or plan upgrade. End users accessing NAT via the SaaS dashboard do not need to call this endpoint directly — it is handled automatically.

Request body:

FieldTypeRequiredDescription
tenant_idstringUnique tenant identifier
emailstringTenant email address
planstringPlan tier: free, pro, team, enterprise
stripe_customer_idstringStripe customer ID
stripe_subscription_idstringStripe subscription ID

Response 200 OK:

{
  "tenant_id": "tenant_abc123",
  "api_key": "nat_pk_...",
  "plan": "pro",
  "status": "active"
}

Error responses:

StatusCodeDescription
400INVALID_REQUESTMissing required fields
409TENANT_EXISTSTenant already exists with a different configuration

See also

Was this helpful?