API/Billing
Start a subscription
POST/v1/billing/checkout-sessions· API key
Returns a link to a Stripe-hosted checkout page for the chosen plan.
Open it in a browser; the CLI's einvoicing upgrade does this for
you. The plan changes when Stripe confirms payment. Watch
GET /v1/account. An account that already has a subscription
manages it through a portal session instead (409).
curl -X POST https://api.einvoicing.dev/v1/billing/checkout-sessions \ -H "Authorization: Bearer $EINVOICING_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "plan": "developer" }'<?php$client = new GuzzleHttp\Client();$response = $client->request('POST', 'https://api.einvoicing.dev/v1/billing/checkout-sessions', [ 'headers' => [ 'Authorization' => 'Bearer ' . getenv('EINVOICING_API_KEY'), 'Accept' => 'application/json', ], 'json' => [ 'plan' => 'developer', ],]);$data = json_decode((string) $response->getBody(), true)['data'];payload := `{ "plan": "developer"}`req, _ := http.NewRequest(http.MethodPost, "https://api.einvoicing.dev/v1/billing/checkout-sessions", strings.NewReader(payload))req.Header.Set("Authorization", "Bearer "+os.Getenv("EINVOICING_API_KEY"))req.Header.Set("Content-Type", "application/json")res, err := http.DefaultClient.Do(req)if err != nil { log.Fatal(err)}defer res.Body.Close()const res = await fetch("https://api.einvoicing.dev/v1/billing/checkout-sessions", { method: "POST", headers: { Authorization: `Bearer ${process.env.EINVOICING_API_KEY}`, "Content-Type": "application/json", }, body: JSON.stringify({ "plan": "developer" }),});const { data } = await res.json();Request body
application/json· required
| Field | Type | Description |
|---|---|---|
planrequired | string | The paid plan to start.
|
{ "plan": "developer"}Response
201 A checkout page was created. The link expires at expires_at.
| Field | Type | Description |
|---|---|---|
datarequired | BillingSession | A link to a Stripe-hosted billing page. |
{ "data": { "url": "https://checkout.stripe.com/c/pay/cs_live_a1B2c3D4", "expires_at": "2026-09-12T14:05:02.117Z" }}Errors
Every error is application/problem+json (RFC 9457). Branch on type, which is stable, never on title or detail.
| Status | When |
|---|---|
| 400 | The body could not be parsed as the XML or JSON its |
| 401 | No key, or a key that is unknown or revoked. Problem |
| 403 | A test key cannot manage keys or billing. Use a live key. Problem
|
| 409 | The account already has a subscription. Create a portal session to change it. Problem |
| 422 | The body parsed and was rejected. Includes unknown fields, which are
refused rather than silently dropped. Problem |
| 429 | Too many requests in a short window. Slow down and retry after the
number of seconds in |
Schemas
CheckoutSessionRequest
The plan to subscribe to.
| Field | Type | Description |
|---|---|---|
planrequired | string | The paid plan to start.
|
BillingSession
A link to a Stripe-hosted billing page.
| Field | Type | Description |
|---|---|---|
urlrequired | string · uri | Open this in a browser. |
expires_atrequired | string or null · date-time | When the link stops working, if Stripe says. |