Skip to content

Billing

Billing is managed via Stripe. The API provides endpoints to initiate checkout sessions, open the customer portal, change subscription tiers, and retrieve billing status and invoice history.

All billing endpoints are under /api/admin/billing and /api/admin/invoices and require a GitHub OAuth Bearer token.


Subscription Tiers

TierMax projectsMonthly API requests
free3200
hobby72,000
pro1520,000

Object: BillingStatus

json
{
  "tier": "hobby",
  "projects": {
    "count": 4,
    "limit": 7
  },
  "currentPeriodEnd": "2025-02-01T00:00:00.000Z",
  "overLimit": [],
  "apiRequests": {
    "count": 847,
    "limit": 2000,
    "resetDate": "2025-02-01T00:00:00.000Z"
  },
  "lastStripeError": null,
  "scheduledTier": null,
  "scheduledChangeDate": null
}
FieldTypeDescription
tierstringCurrent subscription tier: free, hobby, or pro
projectsResourceUsageProject count and limit
currentPeriodEndISO 8601 or nullEnd of the current Stripe billing period
overLimitstring[]Names of resources currently over their limit
apiRequestsApiRequestUsageMonthly API request count, limit, and reset date
lastStripeErrorstring or nullLast Stripe error message, if any
scheduledTierstring or nullTier that takes effect at scheduledChangeDate
scheduledChangeDateISO 8601 or nullWhen the scheduled tier change takes effect

ResourceUsage:

FieldTypeDescription
countintegerCurrent number of resources
limitinteger or nullMaximum allowed; null means unlimited

ApiRequestUsage:

FieldTypeDescription
countintegerRequests used this calendar month
limitinteger or nullMonthly limit; null means unlimited
resetDateISO 8601 datetimeFirst second of next calendar month (UTC)

Object: Invoice

json
{
  "invoiceId": "550e8400-e29b-41d4-a716-446655440000",
  "accountId": "7c9e6679-7425-40de-944b-e07fc1f90ae7",
  "tier": "hobby",
  "amountPaidCents": 900,
  "status": "paid",
  "billingPeriodStart": "2025-01-01T00:00:00.000Z",
  "billingPeriodEnd": "2025-02-01T00:00:00.000Z",
  "createdAt": "2025-01-15T10:30:00.000Z"
}
FieldTypeDescription
invoiceIdUUIDUnique invoice ID
accountIdUUIDOwning account
tierstringSubscription tier at time of invoice
amountPaidCentsintegerAmount charged in cents (e.g. 900 = $9.00)
statusstringInvoice status (e.g. paid, open)
billingPeriodStartISO 8601 datetimeStart of billing period
billingPeriodEndISO 8601 datetimeEnd of billing period
createdAtISO 8601 datetimeWhen the invoice was created

Get Billing Status

Returns current usage, tier, and upcoming changes.

GET /api/admin/billing/status

Auth: Authorization: Bearer <token>

Responses:

StatusDescription
200Returns BillingStatus object
404Account not found

Example:

bash
curl https://app.skystate.io/api/admin/billing/status \
  -H "Authorization: Bearer <token>"

Create Checkout Session

Initiates a Stripe Checkout session for upgrading to a paid tier. Returns a Stripe-hosted URL to redirect the user to.

POST /api/admin/billing/checkout

Auth: Authorization: Bearer <token>

Request body:

json
{
  "tier": "hobby",
  "successUrl": "https://app.skystate.io/settings?upgrade=success",
  "cancelUrl": "https://app.skystate.io/settings"
}
FieldTypeRequiredDescription
tierstringYesTarget tier: hobby or pro
successUrlstringYesURL to redirect to after successful payment
cancelUrlstringYesURL to redirect to if the user cancels

Responses:

StatusBodyDescription
200{ "url": "https://checkout.stripe.com/..." }Checkout URL
400ErrorValidation error or account not found
500ErrorUnexpected error

Example:

bash
curl -X POST https://app.skystate.io/api/admin/billing/checkout \
  -H "Authorization: Bearer <token>" \
  -H "Content-Type: application/json" \
  -d '{"tier":"hobby","successUrl":"https://app.skystate.io?success=1","cancelUrl":"https://app.skystate.io"}'

Open Customer Portal

Creates a Stripe Customer Portal session for managing payment methods, viewing past invoices, and cancelling subscriptions.

POST /api/admin/billing/portal

Auth: Authorization: Bearer <token>

Request body:

json
{
  "returnUrl": "https://app.skystate.io/settings"
}
FieldTypeRequiredDescription
returnUrlstringYesURL to return to after leaving the portal

Responses:

StatusBodyDescription
200{ "url": "https://billing.stripe.com/..." }Portal URL
400ErrorAccount not found or Stripe error
500ErrorUnexpected error

Example:

bash
curl -X POST https://app.skystate.io/api/admin/billing/portal \
  -H "Authorization: Bearer <token>" \
  -H "Content-Type: application/json" \
  -d '{"returnUrl":"https://app.skystate.io/settings"}'

Change Tier

Changes the active subscription tier immediately or schedules a downgrade. Upgrading takes effect immediately; downgrading is scheduled for the end of the current billing period.

POST /api/admin/billing/change-tier

Auth: Authorization: Bearer <token>

Request body:

json
{
  "tier": "pro"
}
FieldTypeRequiredDescription
tierstringYesTarget tier: free, hobby, or pro

Responses:

StatusBodyDescription
200{ "message": "..." }Tier change processed
400ErrorValidation error or account not found
500ErrorUnexpected error

Example:

bash
curl -X POST https://app.skystate.io/api/admin/billing/change-tier \
  -H "Authorization: Bearer <token>" \
  -H "Content-Type: application/json" \
  -d '{"tier":"pro"}'

Cancel Scheduled Downgrade

Cancels a pending tier downgrade that was scheduled for the end of the billing period.

POST /api/admin/billing/cancel-downgrade

Auth: Authorization: Bearer <token>

Request body: None

Responses:

StatusBodyDescription
200{ "message": "..." }Downgrade cancelled
400ErrorAccount not found or no downgrade scheduled
500ErrorUnexpected error

Example:

bash
curl -X POST https://app.skystate.io/api/admin/billing/cancel-downgrade \
  -H "Authorization: Bearer <token>"

List Invoices

Returns all invoices for the authenticated account, ordered by creation date descending.

GET /api/admin/invoices

Auth: Authorization: Bearer <token>

Response: 200 OK — array of Invoice objects.

Example:

bash
curl https://app.skystate.io/api/admin/invoices \
  -H "Authorization: Bearer <token>"

Get Invoice by ID

GET /api/admin/invoices/{invoiceId}

Auth: Authorization: Bearer <token>

Path parameters:

ParameterTypeDescription
invoiceIdUUIDInvoice ID

Responses:

StatusDescription
200Returns Invoice object
404Invoice not found

Example:

bash
curl https://app.skystate.io/api/admin/invoices/550e8400-e29b-41d4-a716-446655440000 \
  -H "Authorization: Bearer <token>"

Built with VitePress