{
  "openapi": "3.1.0",
  "info": {
    "title": "Ledger API",
    "version": "0.1.0"
  },
  "components": {
    "securitySchemes": {
      "ApiKey": {
        "type": "http",
        "scheme": "bearer"
      }
    },
    "responses": {
      "RateLimited": {
        "description": "Too many requests. Wait Retry-After seconds, then retry. See the rate limits guide.",
        "headers": {
          "Retry-After": {
            "description": "Seconds until a request is allowed again",
            "schema": {
              "type": "integer"
            }
          },
          "RateLimit-Limit": {
            "description": "The burst: requests allowed at once",
            "schema": {
              "type": "integer"
            }
          },
          "RateLimit-Remaining": {
            "description": "Requests left right now",
            "schema": {
              "type": "integer"
            }
          },
          "RateLimit-Reset": {
            "description": "Seconds until the full burst is back",
            "schema": {
              "type": "integer"
            }
          },
          "RateLimit-Policy": {
            "description": "The sustained rate per second and the burst, such as 200;w=1;burst=1000",
            "schema": {
              "type": "string"
            }
          }
        },
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/RateLimitError"
            }
          }
        }
      },
      "PayloadTooLarge": {
        "description": "The request body is over the limit: 1 MiB, or 5 MiB for a webhook.",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/PayloadTooLargeError"
            }
          }
        }
      }
    },
    "schemas": {
      "RateLimitError": {
        "type": "object",
        "properties": {
          "error": {
            "type": "string",
            "example": "rate_limited"
          }
        },
        "required": [
          "error"
        ]
      },
      "PayloadTooLargeError": {
        "type": "object",
        "properties": {
          "error": {
            "type": "string",
            "example": "payload_too_large"
          }
        },
        "required": [
          "error"
        ]
      },
      "CatalogPublishResponse": {
        "type": "object",
        "properties": {
          "changed": {
            "type": "boolean"
          },
          "stripeSync": {
            "type": "string",
            "enum": [
              "synced",
              "pending",
              "failed",
              "awaiting_connection"
            ]
          }
        },
        "required": [
          "changed",
          "stripeSync"
        ]
      },
      "CatalogError": {
        "type": "object",
        "properties": {
          "error": {
            "type": "string"
          },
          "issues": {
            "type": "array",
            "items": {
              "type": "object",
              "properties": {
                "path": {
                  "type": "string"
                },
                "message": {
                  "type": "string"
                }
              },
              "required": [
                "path",
                "message"
              ]
            }
          }
        },
        "required": [
          "error"
        ]
      },
      "CatalogInput": {
        "type": "object",
        "properties": {
          "schemaVersion": {
            "type": "integer",
            "enum": [
              1
            ]
          },
          "environment": {
            "type": "string",
            "enum": [
              "sandbox",
              "live"
            ]
          },
          "currency": {
            "type": "string",
            "enum": [
              "usd"
            ]
          },
          "plans": {
            "type": "array",
            "items": {
              "type": "object",
              "properties": {
                "id": {
                  "type": "string",
                  "minLength": 1,
                  "maxLength": 100,
                  "pattern": "^[a-zA-Z0-9_-]+$"
                },
                "name": {
                  "type": "string",
                  "minLength": 1,
                  "maxLength": 120
                },
                "monthlyCredits": {
                  "type": "integer",
                  "exclusiveMinimum": 0,
                  "maximum": 2147483647
                },
                "monthlyPriceCents": {
                  "type": "integer",
                  "minimum": 0,
                  "maximum": 2147483647
                },
                "overageUnitAmountCents": {
                  "type": "integer",
                  "nullable": true,
                  "maximum": 2147483647,
                  "exclusiveMinimum": 0,
                  "description": "Price in cents per overageBillingUnits credits used after the plan's credits and packs run out. Null or omitted: no overage."
                },
                "overageBillingUnits": {
                  "type": "integer",
                  "exclusiveMinimum": 0,
                  "maximum": 2147483647,
                  "description": "Credits billed per overageUnitAmountCents. Defaults to 1."
                },
                "overageCapCents": {
                  "type": "integer",
                  "nullable": true,
                  "minimum": 0,
                  "maximum": 2147483647,
                  "description": "Default monthly overage spend cap in cents. A customer's own cap replaces it. Null or omitted: no cap."
                }
              },
              "required": [
                "id",
                "name",
                "monthlyCredits",
                "monthlyPriceCents"
              ],
              "additionalProperties": false
            },
            "minItems": 1
          },
          "features": {
            "type": "array",
            "items": {
              "type": "object",
              "properties": {
                "id": {
                  "type": "string",
                  "minLength": 1,
                  "maxLength": 100,
                  "pattern": "^[a-zA-Z0-9_-]+$"
                },
                "name": {
                  "type": "string",
                  "minLength": 1,
                  "maxLength": 120
                },
                "creditCost": {
                  "type": "integer",
                  "exclusiveMinimum": 0,
                  "maximum": 2147483647
                }
              },
              "required": [
                "id",
                "name",
                "creditCost"
              ],
              "additionalProperties": false
            },
            "minItems": 1
          },
          "addOns": {
            "type": "array",
            "items": {
              "type": "object",
              "properties": {
                "id": {
                  "type": "string",
                  "minLength": 1,
                  "maxLength": 100,
                  "pattern": "^[a-zA-Z0-9_-]+$"
                },
                "name": {
                  "type": "string",
                  "minLength": 1,
                  "maxLength": 120
                },
                "type": {
                  "type": "string",
                  "enum": [
                    "credit_pack"
                  ]
                },
                "creditAmount": {
                  "type": "integer",
                  "exclusiveMinimum": 0,
                  "maximum": 2147483647
                },
                "priceCents": {
                  "type": "integer",
                  "maximum": 2147483647,
                  "exclusiveMinimum": 0
                }
              },
              "required": [
                "id",
                "name",
                "type",
                "creditAmount",
                "priceCents"
              ],
              "additionalProperties": false
            }
          },
          "policy": {
            "type": "object",
            "properties": {
              "onInsufficientCredits": {
                "type": "string",
                "enum": [
                  "block"
                ]
              }
            },
            "required": [
              "onInsufficientCredits"
            ],
            "additionalProperties": false
          }
        },
        "required": [
          "schemaVersion",
          "environment",
          "currency",
          "plans",
          "features",
          "addOns",
          "policy"
        ],
        "additionalProperties": false
      },
      "FeatureList": {
        "type": "object",
        "properties": {
          "items": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/FeatureItem"
            }
          }
        },
        "required": [
          "items"
        ]
      },
      "FeatureItem": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "minLength": 1,
            "maxLength": 100,
            "pattern": "^[a-zA-Z0-9_-]+$"
          },
          "name": {
            "type": "string",
            "minLength": 1,
            "maxLength": 120
          },
          "creditCost": {
            "type": "integer",
            "exclusiveMinimum": 0,
            "maximum": 2147483647
          }
        },
        "required": [
          "id",
          "name",
          "creditCost"
        ],
        "additionalProperties": false
      },
      "FeatureMutation": {
        "type": "object",
        "properties": {
          "item": {
            "$ref": "#/components/schemas/FeatureItem"
          },
          "changed": {
            "type": "boolean"
          }
        },
        "required": [
          "item",
          "changed"
        ]
      },
      "CreateFeatureInput": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "minLength": 1,
            "maxLength": 100,
            "pattern": "^[a-zA-Z0-9_-]+$"
          },
          "name": {
            "type": "string",
            "minLength": 1,
            "maxLength": 120
          },
          "creditCost": {
            "type": "integer",
            "exclusiveMinimum": 0,
            "maximum": 2147483647
          }
        },
        "required": [
          "id",
          "name",
          "creditCost"
        ],
        "additionalProperties": false
      },
      "UpdateFeatureInput": {
        "type": "object",
        "properties": {
          "name": {
            "type": "string",
            "minLength": 1,
            "maxLength": 120
          },
          "creditCost": {
            "type": "integer",
            "exclusiveMinimum": 0,
            "maximum": 2147483647
          }
        },
        "required": [
          "name",
          "creditCost"
        ],
        "additionalProperties": false
      },
      "FeatureArchived": {
        "type": "object",
        "properties": {
          "archived": {
            "type": "boolean"
          }
        },
        "required": [
          "archived"
        ]
      },
      "PlanList": {
        "type": "object",
        "properties": {
          "items": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/PlanItem"
            }
          }
        },
        "required": [
          "items"
        ]
      },
      "PlanItem": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "minLength": 1,
            "maxLength": 100,
            "pattern": "^[a-zA-Z0-9_-]+$"
          },
          "name": {
            "type": "string",
            "minLength": 1,
            "maxLength": 120
          },
          "monthlyCredits": {
            "type": "integer",
            "exclusiveMinimum": 0,
            "maximum": 2147483647
          },
          "monthlyPriceCents": {
            "type": "integer",
            "minimum": 0,
            "maximum": 2147483647
          },
          "overageUnitAmountCents": {
            "type": "integer",
            "nullable": true,
            "maximum": 2147483647,
            "exclusiveMinimum": 0,
            "description": "Price in cents per overageBillingUnits credits used after the plan's credits and packs run out. Null or omitted: no overage."
          },
          "overageBillingUnits": {
            "type": "integer",
            "exclusiveMinimum": 0,
            "maximum": 2147483647,
            "description": "Credits billed per overageUnitAmountCents. Defaults to 1."
          },
          "overageCapCents": {
            "type": "integer",
            "nullable": true,
            "minimum": 0,
            "maximum": 2147483647,
            "description": "Default monthly overage spend cap in cents. A customer's own cap replaces it. Null or omitted: no cap."
          },
          "stripeSyncStatus": {
            "type": "string",
            "nullable": true,
            "enum": [
              "pending",
              "synced",
              "failed",
              null
            ]
          }
        },
        "required": [
          "id",
          "name",
          "monthlyCredits",
          "monthlyPriceCents"
        ],
        "additionalProperties": false
      },
      "PlanMutation": {
        "type": "object",
        "properties": {
          "item": {
            "$ref": "#/components/schemas/PlanItem"
          },
          "changed": {
            "type": "boolean"
          },
          "stripeSync": {
            "type": "string",
            "enum": [
              "synced",
              "pending",
              "failed",
              "awaiting_connection"
            ]
          }
        },
        "required": [
          "item",
          "changed"
        ]
      },
      "CreatePlanInput": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "minLength": 1,
            "maxLength": 100,
            "pattern": "^[a-zA-Z0-9_-]+$"
          },
          "name": {
            "type": "string",
            "minLength": 1,
            "maxLength": 120
          },
          "monthlyCredits": {
            "type": "integer",
            "exclusiveMinimum": 0,
            "maximum": 2147483647
          },
          "monthlyPriceCents": {
            "type": "integer",
            "minimum": 0,
            "maximum": 2147483647
          },
          "overageUnitAmountCents": {
            "type": "integer",
            "nullable": true,
            "maximum": 2147483647,
            "exclusiveMinimum": 0,
            "description": "Price in cents per overageBillingUnits credits used after the plan's credits and packs run out. Null or omitted: no overage."
          },
          "overageBillingUnits": {
            "type": "integer",
            "exclusiveMinimum": 0,
            "maximum": 2147483647,
            "description": "Credits billed per overageUnitAmountCents. Defaults to 1."
          },
          "overageCapCents": {
            "type": "integer",
            "nullable": true,
            "minimum": 0,
            "maximum": 2147483647,
            "description": "Default monthly overage spend cap in cents. A customer's own cap replaces it. Null or omitted: no cap."
          }
        },
        "required": [
          "id",
          "name",
          "monthlyCredits",
          "monthlyPriceCents"
        ],
        "additionalProperties": false
      },
      "UpdatePlanInput": {
        "type": "object",
        "properties": {
          "name": {
            "type": "string",
            "minLength": 1,
            "maxLength": 120
          },
          "monthlyCredits": {
            "type": "integer",
            "exclusiveMinimum": 0,
            "maximum": 2147483647
          },
          "monthlyPriceCents": {
            "type": "integer",
            "minimum": 0,
            "maximum": 2147483647
          },
          "overageUnitAmountCents": {
            "type": "integer",
            "nullable": true,
            "maximum": 2147483647,
            "exclusiveMinimum": 0,
            "description": "Price in cents per overageBillingUnits credits used after the plan's credits and packs run out. Null or omitted: no overage."
          },
          "overageBillingUnits": {
            "type": "integer",
            "exclusiveMinimum": 0,
            "maximum": 2147483647,
            "description": "Credits billed per overageUnitAmountCents. Defaults to 1."
          },
          "overageCapCents": {
            "type": "integer",
            "nullable": true,
            "minimum": 0,
            "maximum": 2147483647,
            "description": "Default monthly overage spend cap in cents. A customer's own cap replaces it. Null or omitted: no cap."
          }
        },
        "required": [
          "name",
          "monthlyCredits",
          "monthlyPriceCents"
        ],
        "additionalProperties": false
      },
      "PlanArchived": {
        "type": "object",
        "properties": {
          "archived": {
            "type": "boolean"
          }
        },
        "required": [
          "archived"
        ]
      },
      "AddOnList": {
        "type": "object",
        "properties": {
          "items": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/AddOnItem"
            }
          }
        },
        "required": [
          "items"
        ]
      },
      "AddOnItem": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "minLength": 1,
            "maxLength": 100,
            "pattern": "^[a-zA-Z0-9_-]+$"
          },
          "name": {
            "type": "string",
            "minLength": 1,
            "maxLength": 120
          },
          "type": {
            "type": "string",
            "enum": [
              "credit_pack"
            ]
          },
          "creditAmount": {
            "type": "integer",
            "exclusiveMinimum": 0,
            "maximum": 2147483647
          },
          "priceCents": {
            "type": "integer",
            "maximum": 2147483647,
            "exclusiveMinimum": 0
          }
        },
        "required": [
          "id",
          "name",
          "type",
          "creditAmount",
          "priceCents"
        ],
        "additionalProperties": false
      },
      "AddOnMutation": {
        "type": "object",
        "properties": {
          "item": {
            "$ref": "#/components/schemas/AddOnItem"
          },
          "changed": {
            "type": "boolean"
          }
        },
        "required": [
          "item",
          "changed"
        ]
      },
      "CreateAddOnInput": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "minLength": 1,
            "maxLength": 100,
            "pattern": "^[a-zA-Z0-9_-]+$"
          },
          "name": {
            "type": "string",
            "minLength": 1,
            "maxLength": 120
          },
          "type": {
            "type": "string",
            "enum": [
              "credit_pack"
            ]
          },
          "creditAmount": {
            "type": "integer",
            "exclusiveMinimum": 0,
            "maximum": 2147483647
          },
          "priceCents": {
            "type": "integer",
            "maximum": 2147483647,
            "exclusiveMinimum": 0
          }
        },
        "required": [
          "id",
          "name",
          "type",
          "creditAmount",
          "priceCents"
        ],
        "additionalProperties": false
      },
      "UpdateAddOnInput": {
        "type": "object",
        "properties": {
          "name": {
            "type": "string",
            "minLength": 1,
            "maxLength": 120
          },
          "type": {
            "type": "string",
            "enum": [
              "credit_pack"
            ]
          },
          "creditAmount": {
            "type": "integer",
            "exclusiveMinimum": 0,
            "maximum": 2147483647
          },
          "priceCents": {
            "type": "integer",
            "maximum": 2147483647,
            "exclusiveMinimum": 0
          }
        },
        "required": [
          "name",
          "type",
          "creditAmount",
          "priceCents"
        ],
        "additionalProperties": false
      },
      "AddOnArchived": {
        "type": "object",
        "properties": {
          "archived": {
            "type": "boolean"
          }
        },
        "required": [
          "archived"
        ]
      },
      "CheckResponse": {
        "type": "object",
        "properties": {
          "allowed": {
            "type": "boolean",
            "description": "Whether track would accept this feature now: from prepaid credits, or as overage within the cap on a paid plan with overage."
          },
          "availableCredits": {
            "type": "integer",
            "minimum": 0
          },
          "creditCost": {
            "type": "integer",
            "exclusiveMinimum": 0
          }
        },
        "required": [
          "allowed",
          "availableCredits",
          "creditCost"
        ]
      },
      "CheckError": {
        "type": "object",
        "properties": {
          "error": {
            "type": "string"
          }
        },
        "required": [
          "error"
        ]
      },
      "CheckInput": {
        "type": "object",
        "properties": {
          "customerId": {
            "type": "string",
            "minLength": 1,
            "maxLength": 255
          },
          "featureId": {
            "type": "string",
            "minLength": 1,
            "maxLength": 100
          }
        },
        "required": [
          "customerId",
          "featureId"
        ],
        "additionalProperties": false
      },
      "CustomerSubscriptionPage": {
        "type": "object",
        "properties": {
          "items": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/CustomerSubscriptionItem"
            }
          },
          "hasMore": {
            "type": "boolean"
          },
          "nextCursor": {
            "type": "integer",
            "nullable": true
          }
        },
        "required": [
          "items",
          "hasMore",
          "nextCursor"
        ]
      },
      "CustomerSubscriptionItem": {
        "type": "object",
        "properties": {
          "id": {
            "type": "integer"
          },
          "planId": {
            "type": "string"
          },
          "planName": {
            "type": "string"
          },
          "monthlyPriceCents": {
            "type": "integer"
          },
          "status": {
            "type": "string"
          },
          "createdAt": {
            "type": "string",
            "format": "date-time"
          },
          "currentPeriodStart": {
            "type": "string",
            "nullable": true,
            "format": "date-time"
          },
          "currentPeriodEnd": {
            "type": "string",
            "nullable": true,
            "format": "date-time"
          },
          "cancellation": {
            "$ref": "#/components/schemas/CustomerSubscriptionCancellation"
          }
        },
        "required": [
          "id",
          "planId",
          "planName",
          "monthlyPriceCents",
          "status",
          "createdAt",
          "currentPeriodStart",
          "currentPeriodEnd",
          "cancellation"
        ]
      },
      "CustomerSubscriptionCancellation": {
        "type": "object",
        "nullable": true,
        "properties": {
          "cancelAtPeriodEnd": {
            "type": "boolean"
          },
          "scheduledEnd": {
            "type": "boolean"
          },
          "manageable": {
            "type": "boolean"
          },
          "cancelsAt": {
            "type": "string",
            "nullable": true,
            "format": "date-time"
          }
        },
        "required": [
          "cancelAtPeriodEnd",
          "scheduledEnd",
          "manageable",
          "cancelsAt"
        ]
      },
      "CustomerError": {
        "type": "object",
        "properties": {
          "error": {
            "type": "string"
          }
        },
        "required": [
          "error"
        ]
      },
      "CustomerPurchasePage": {
        "type": "object",
        "properties": {
          "items": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/CustomerPurchaseItem"
            }
          },
          "hasMore": {
            "type": "boolean"
          },
          "nextCursor": {
            "type": "integer",
            "nullable": true
          }
        },
        "required": [
          "items",
          "hasMore",
          "nextCursor"
        ]
      },
      "CustomerPurchaseItem": {
        "type": "object",
        "properties": {
          "id": {
            "type": "integer"
          },
          "addOnId": {
            "type": "string"
          },
          "name": {
            "type": "string"
          },
          "creditAmount": {
            "type": "integer",
            "nullable": true
          },
          "status": {
            "type": "string"
          },
          "createdAt": {
            "type": "string",
            "format": "date-time"
          }
        },
        "required": [
          "id",
          "addOnId",
          "name",
          "creditAmount",
          "status",
          "createdAt"
        ]
      },
      "UpdateCustomerSubscriptionCancellationResponse": {
        "type": "object",
        "properties": {
          "cancelAtPeriodEnd": {
            "type": "boolean"
          },
          "cancelsAt": {
            "type": "string",
            "nullable": true,
            "format": "date-time"
          }
        },
        "required": [
          "cancelAtPeriodEnd",
          "cancelsAt"
        ]
      },
      "UpdateCustomerSubscriptionCancellationInput": {
        "type": "object",
        "properties": {
          "cancelAtPeriodEnd": {
            "type": "boolean"
          }
        },
        "required": [
          "cancelAtPeriodEnd"
        ],
        "additionalProperties": false
      },
      "CustomerDeleted": {
        "type": "object",
        "properties": {
          "customerId": {
            "type": "string"
          },
          "deleted": {
            "type": "boolean",
            "const": true
          }
        },
        "required": [
          "customerId",
          "deleted"
        ]
      },
      "CustomerOverageCap": {
        "type": "object",
        "properties": {
          "customerId": {
            "type": "string"
          },
          "capCents": {
            "type": "integer",
            "nullable": true,
            "minimum": 0
          },
          "overage": {
            "$ref": "#/components/schemas/CustomerOverage"
          }
        },
        "required": [
          "customerId",
          "capCents",
          "overage"
        ]
      },
      "CustomerOverage": {
        "type": "object",
        "nullable": true,
        "properties": {
          "credits": {
            "type": "integer",
            "minimum": 0,
            "description": "Overage credits recorded in the current billing period."
          },
          "costCents": {
            "type": "integer",
            "minimum": 0,
            "description": "This period's overage cost: ceil(credits / billingUnits) × unitAmountCents per recorded rate."
          },
          "unitAmountCents": {
            "type": "integer",
            "exclusiveMinimum": 0
          },
          "billingUnits": {
            "type": "integer",
            "exclusiveMinimum": 0
          },
          "capCents": {
            "type": "integer",
            "nullable": true,
            "minimum": 0,
            "description": "Effective monthly overage cap. Null: no cap."
          },
          "capSource": {
            "type": "string",
            "nullable": true,
            "enum": [
              "customer",
              "override",
              "plan",
              null
            ],
            "description": "customer: the customer's own cap; override: the customer's overage override; plan: the plan default; null: no cap."
          },
          "remainingCents": {
            "type": "integer",
            "nullable": true,
            "minimum": 0
          },
          "source": {
            "type": "string",
            "enum": [
              "plan",
              "override"
            ],
            "description": "Where the overage rate comes from: the plan, or an override set with setCustomerOverageOverride."
          },
          "override": {
            "$ref": "#/components/schemas/CustomerOverageOverride"
          },
          "periodStart": {
            "type": "string",
            "format": "date-time"
          },
          "periodEnd": {
            "type": "string",
            "format": "date-time"
          }
        },
        "required": [
          "credits",
          "costCents",
          "unitAmountCents",
          "billingUnits",
          "capCents",
          "capSource",
          "remainingCents",
          "source",
          "override",
          "periodStart",
          "periodEnd"
        ]
      },
      "CustomerOverageOverride": {
        "type": "object",
        "nullable": true,
        "properties": {
          "planId": {
            "type": "string",
            "nullable": true,
            "description": "The plan it applies to. Null: any plan."
          },
          "unitAmountCents": {
            "type": "integer",
            "exclusiveMinimum": 0
          },
          "billingUnits": {
            "type": "integer",
            "exclusiveMinimum": 0
          },
          "capCents": {
            "type": "integer",
            "nullable": true,
            "minimum": 0,
            "description": "The override's cap. Null: the plan default applies."
          },
          "effectiveFrom": {
            "type": "string",
            "format": "date-time"
          }
        },
        "required": [
          "planId",
          "unitAmountCents",
          "billingUnits",
          "capCents",
          "effectiveFrom"
        ],
        "description": "The override behind the rate when source is override. Null otherwise."
      },
      "SetCustomerOverageCapInput": {
        "type": "object",
        "properties": {
          "capCents": {
            "type": "integer",
            "nullable": true,
            "minimum": 0,
            "maximum": 2147483647,
            "description": "The customer's monthly overage cap in cents. It replaces the plan default, higher or lower. Null clears it and restores the plan default."
          }
        },
        "required": [
          "capCents"
        ],
        "additionalProperties": false
      },
      "CustomerOverageOverrideResult": {
        "type": "object",
        "properties": {
          "customerId": {
            "type": "string"
          },
          "planId": {
            "type": "string",
            "nullable": true,
            "description": "The scope that was set or cleared. Null: any plan."
          },
          "override": {
            "allOf": [
              {
                "$ref": "#/components/schemas/CustomerOverageOverride"
              },
              {
                "description": "The override now in force for this planId scope. Null once cleared."
              }
            ]
          },
          "overage": {
            "allOf": [
              {
                "$ref": "#/components/schemas/CustomerOverage"
              },
              {
                "description": "The effective overage terms on the customer's active paid plan, after this change."
              }
            ]
          }
        },
        "required": [
          "customerId",
          "planId",
          "override",
          "overage"
        ]
      },
      "SetCustomerOverageOverrideInput": {
        "type": "object",
        "properties": {
          "planId": {
            "type": "string",
            "nullable": true,
            "minLength": 1,
            "maxLength": 255,
            "description": "The plan this override applies to. Omit or null to apply it to any plan; a plan-specific override beats one for any plan."
          },
          "unitAmountCents": {
            "type": "integer",
            "nullable": true,
            "exclusiveMinimum": 0,
            "maximum": 2147483647,
            "description": "Negotiated price in cents per billingUnits overage credits. Null clears the override for this planId and restores the plan rate."
          },
          "billingUnits": {
            "type": "integer",
            "exclusiveMinimum": 0,
            "maximum": 2147483647,
            "description": "Credits billed per unitAmountCents. Defaults to 1. Omit when clearing."
          },
          "capCents": {
            "type": "integer",
            "nullable": true,
            "minimum": 0,
            "maximum": 2147483647,
            "description": "Monthly overage cap in cents for this override. Omit or null to keep the plan default. The customer's own cap (setCustomerOverageCap) still wins. Omit when clearing."
          }
        },
        "required": [
          "unitAmountCents"
        ],
        "additionalProperties": false
      },
      "BalanceAdjustmentResult": {
        "type": "object",
        "properties": {
          "customerId": {
            "type": "string"
          },
          "adjustment": {
            "$ref": "#/components/schemas/BalanceAdjustment"
          },
          "availableCredits": {
            "type": "integer",
            "minimum": 0,
            "description": "The customer's balance after the adjustment."
          }
        },
        "required": [
          "customerId",
          "adjustment",
          "availableCredits"
        ]
      },
      "BalanceAdjustment": {
        "type": "object",
        "properties": {
          "id": {
            "type": "integer",
            "exclusiveMinimum": 0
          },
          "credits": {
            "type": "integer",
            "description": "Positive: credits added. Negative: credits removed."
          },
          "reason": {
            "type": "string"
          },
          "expiresAt": {
            "type": "string",
            "nullable": true,
            "format": "date-time",
            "description": "When added credits stop counting. Null: they never expire, or credits were removed."
          },
          "stripeInvoiceId": {
            "type": "string",
            "nullable": true
          },
          "stripeCheckoutSessionId": {
            "type": "string",
            "nullable": true
          },
          "createdBy": {
            "type": "string",
            "description": "Who made it: api_key:<key ID>, api_key:workspace or dashboard:<user ID>."
          },
          "createdAt": {
            "type": "string",
            "format": "date-time"
          }
        },
        "required": [
          "id",
          "credits",
          "reason",
          "expiresAt",
          "stripeInvoiceId",
          "stripeCheckoutSessionId",
          "createdBy",
          "createdAt"
        ]
      },
      "BalanceAdjustmentError": {
        "type": "object",
        "properties": {
          "error": {
            "type": "string"
          },
          "availableCredits": {
            "type": "integer",
            "minimum": 0,
            "description": "With insufficient_credits: the most this adjustment can remove."
          }
        },
        "required": [
          "error"
        ]
      },
      "BalanceAdjustmentInput": {
        "type": "object",
        "properties": {
          "credits": {
            "type": "integer",
            "minimum": -1000000000,
            "maximum": 1000000000,
            "description": "Credits to add (positive) or remove (negative). A removal never takes the balance below zero.",
            "example": 500
          },
          "reason": {
            "type": "string",
            "minLength": 1,
            "maxLength": 500,
            "description": "Why the balance changed, for your team. Shown on the customer page and in billing history.",
            "example": "Goodwill credit for the Nov 6 outage"
          },
          "idempotencyKey": {
            "type": "string",
            "minLength": 1,
            "maxLength": 255,
            "description": "Your unique key for this adjustment. Retrying with the same key and body returns the first adjustment; the same key with a different body is refused.",
            "example": "goodwill-2026-11-06-user_123"
          },
          "expiresAt": {
            "type": "string",
            "format": "date-time",
            "description": "Added credits only: when they stop counting. Must be in the future. Omit for credits that never expire."
          },
          "stripeInvoiceId": {
            "type": "string",
            "pattern": "^in_[A-Za-z0-9]{1,250}$",
            "description": "Added credits only: the paid Stripe invoice these credits stand in for, when Ledger didn't grant it. Ledger checks it's paid and this customer's, and marks it granted.",
            "example": "in_1QxYzExample"
          },
          "stripeCheckoutSessionId": {
            "type": "string",
            "pattern": "^cs_[A-Za-z0-9_]{1,250}$",
            "description": "Added credits only: the paid credit-pack Checkout Session these credits stand in for. Not with stripeInvoiceId."
          }
        },
        "required": [
          "credits",
          "reason",
          "idempotencyKey"
        ],
        "additionalProperties": false
      },
      "CustomerDetail": {
        "type": "object",
        "properties": {
          "customerId": {
            "type": "string"
          },
          "createdAt": {
            "type": "string",
            "format": "date-time"
          },
          "availableCredits": {
            "type": "integer"
          },
          "currentPlan": {
            "type": "object",
            "nullable": true,
            "properties": {
              "id": {
                "type": "string"
              },
              "name": {
                "type": "string"
              },
              "status": {
                "type": "string",
                "enum": [
                  "active",
                  "past_due",
                  "unpaid",
                  "paused",
                  "pending_checkout"
                ],
                "description": "active: paid up. past_due, unpaid, paused: Stripe is waiting on a payment; the plan stays, without overage, upgrades or early renewal credits. pending_checkout: Checkout hasn't completed."
              },
              "currentPeriodStart": {
                "type": "string",
                "nullable": true,
                "format": "date-time"
              },
              "currentPeriodEnd": {
                "type": "string",
                "nullable": true,
                "format": "date-time"
              }
            },
            "required": [
              "id",
              "name",
              "status",
              "currentPeriodStart",
              "currentPeriodEnd"
            ]
          },
          "overageCapCents": {
            "type": "integer",
            "nullable": true,
            "minimum": 0,
            "description": "The customer's own overage cap, set with setCustomerOverageCap. Null: the plan default applies."
          },
          "overage": {
            "allOf": [
              {
                "$ref": "#/components/schemas/CustomerOverage"
              },
              {
                "description": "Overage this period on the active paid plan. Null when that plan has no overage."
              }
            ]
          },
          "dispute": {
            "$ref": "#/components/schemas/CustomerDispute"
          }
        },
        "required": [
          "customerId",
          "createdAt",
          "availableCredits",
          "currentPlan",
          "overageCapCents",
          "overage",
          "dispute"
        ]
      },
      "CustomerDispute": {
        "type": "object",
        "nullable": true,
        "properties": {
          "status": {
            "type": "string",
            "enum": [
              "open",
              "won",
              "lost"
            ],
            "description": "open: Stripe hasn't closed it. won: closed with no funds lost (including a closed inquiry). lost: the funds went back to the cardholder."
          },
          "amountCents": {
            "type": "integer",
            "minimum": 0
          },
          "createdAt": {
            "type": "string",
            "format": "date-time"
          },
          "closedAt": {
            "type": "string",
            "nullable": true,
            "format": "date-time"
          }
        },
        "required": [
          "status",
          "amountCents",
          "createdAt",
          "closedAt"
        ],
        "description": "The customer's most serious payment dispute: any open one, else the latest lost one, else the latest won. Null: never disputed."
      },
      "CustomerResponse": {
        "type": "object",
        "properties": {
          "customerId": {
            "type": "string"
          },
          "created": {
            "type": "boolean"
          }
        },
        "required": [
          "customerId",
          "created"
        ]
      },
      "CreateCustomerInput": {
        "type": "object",
        "properties": {
          "customerId": {
            "type": "string",
            "minLength": 1,
            "maxLength": 255
          }
        },
        "required": [
          "customerId"
        ],
        "additionalProperties": false
      },
      "StripeCustomerLink": {
        "type": "object",
        "properties": {
          "customerId": {
            "type": "string"
          },
          "stripeCustomerId": {
            "type": "string"
          }
        },
        "required": [
          "customerId",
          "stripeCustomerId"
        ]
      },
      "InternalError": {
        "type": "object",
        "properties": {
          "error": {
            "type": "string",
            "example": "internal_error"
          },
          "request_id": {
            "type": "string",
            "nullable": true
          }
        },
        "required": [
          "error",
          "request_id"
        ]
      },
      "StripeCustomerLinkInput": {
        "type": "object",
        "properties": {
          "stripeCustomerId": {
            "type": "string",
            "maxLength": 255,
            "description": "The customer's existing Stripe customer, in the Stripe account connected for this environment."
          }
        },
        "required": [
          "stripeCustomerId"
        ],
        "additionalProperties": false
      },
      "TrackResponse": {
        "type": "object",
        "properties": {
          "allowed": {
            "type": "boolean",
            "const": true
          },
          "availableCredits": {
            "type": "integer",
            "minimum": 0,
            "description": "Prepaid credits left in plan and credit-pack grants. Overage never makes this negative."
          },
          "chargedCredits": {
            "type": "integer",
            "exclusiveMinimum": 0
          },
          "overageCredits": {
            "type": "integer",
            "minimum": 0,
            "description": "Overage credits recorded in the current billing period, including this request."
          },
          "overageRemainingCents": {
            "type": "integer",
            "nullable": true,
            "minimum": 0,
            "description": "Overage spend left under the effective cap this period. Null when there is no cap or no overage."
          },
          "request_id": {
            "type": "string"
          },
          "idempotent": {
            "type": "boolean"
          }
        },
        "required": [
          "allowed",
          "availableCredits",
          "chargedCredits",
          "overageCredits",
          "overageRemainingCents",
          "request_id",
          "idempotent"
        ]
      },
      "TrackError": {
        "type": "object",
        "properties": {
          "error": {
            "type": "string"
          },
          "availableCredits": {
            "type": "integer",
            "minimum": 0
          },
          "creditCost": {
            "type": "integer",
            "exclusiveMinimum": 0
          },
          "overageRemainingCents": {
            "type": "integer",
            "nullable": true,
            "minimum": 0
          }
        },
        "required": [
          "error"
        ]
      },
      "TrackInput": {
        "type": "object",
        "properties": {
          "customerId": {
            "type": "string",
            "minLength": 1,
            "maxLength": 255
          },
          "featureId": {
            "type": "string",
            "minLength": 1,
            "maxLength": 100
          },
          "request_id": {
            "type": "string",
            "minLength": 1,
            "maxLength": 255
          }
        },
        "required": [
          "customerId",
          "featureId",
          "request_id"
        ],
        "additionalProperties": false
      },
      "AttachResponse": {
        "anyOf": [
          {
            "$ref": "#/components/schemas/PlanAttachResponse"
          },
          {
            "$ref": "#/components/schemas/AddOnAttachResponse"
          },
          {
            "$ref": "#/components/schemas/FreePlanAttachResponse"
          }
        ]
      },
      "PlanAttachResponse": {
        "type": "object",
        "properties": {
          "checkoutUrl": {
            "type": "string",
            "format": "uri"
          },
          "subscriptionId": {
            "type": "integer"
          }
        },
        "required": [
          "checkoutUrl",
          "subscriptionId"
        ],
        "additionalProperties": false
      },
      "AddOnAttachResponse": {
        "type": "object",
        "properties": {
          "checkoutUrl": {
            "type": "string",
            "format": "uri"
          },
          "addOnPurchaseId": {
            "type": "integer"
          }
        },
        "required": [
          "checkoutUrl",
          "addOnPurchaseId"
        ],
        "additionalProperties": false
      },
      "FreePlanAttachResponse": {
        "type": "object",
        "properties": {
          "subscriptionId": {
            "type": "integer"
          },
          "status": {
            "type": "string",
            "enum": [
              "active"
            ]
          }
        },
        "required": [
          "subscriptionId",
          "status"
        ],
        "additionalProperties": false
      },
      "AttachError": {
        "type": "object",
        "properties": {
          "error": {
            "type": "string"
          }
        },
        "required": [
          "error"
        ]
      },
      "AttachInput": {
        "anyOf": [
          {
            "$ref": "#/components/schemas/PlanAttachInput"
          },
          {
            "$ref": "#/components/schemas/AddOnAttachInput"
          }
        ]
      },
      "PlanAttachInput": {
        "type": "object",
        "properties": {
          "customerId": {
            "type": "string",
            "minLength": 1,
            "maxLength": 255
          },
          "planId": {
            "type": "string",
            "minLength": 1,
            "maxLength": 255
          }
        },
        "required": [
          "customerId",
          "planId"
        ],
        "additionalProperties": false
      },
      "AddOnAttachInput": {
        "type": "object",
        "properties": {
          "customerId": {
            "type": "string",
            "minLength": 1,
            "maxLength": 255
          },
          "addOnId": {
            "type": "string",
            "minLength": 1,
            "maxLength": 255
          }
        },
        "required": [
          "customerId",
          "addOnId"
        ],
        "additionalProperties": false
      },
      "UpgradeResponse": {
        "type": "object",
        "properties": {
          "upgradeId": {
            "type": "integer"
          },
          "status": {
            "type": "string",
            "enum": [
              "applied",
              "pending_payment",
              "processing",
              "closed"
            ]
          },
          "paymentUrl": {
            "type": "string",
            "nullable": true,
            "format": "uri"
          }
        },
        "required": [
          "upgradeId",
          "status",
          "paymentUrl"
        ]
      },
      "UpgradeError": {
        "type": "object",
        "properties": {
          "error": {
            "type": "string"
          }
        },
        "required": [
          "error"
        ]
      },
      "UpgradeInput": {
        "type": "object",
        "properties": {
          "customerId": {
            "type": "string",
            "minLength": 1,
            "maxLength": 255
          },
          "planId": {
            "type": "string",
            "minLength": 1,
            "maxLength": 255
          }
        },
        "required": [
          "customerId",
          "planId"
        ],
        "additionalProperties": false
      },
      "ImportSubscriptionResponse": {
        "type": "object",
        "properties": {
          "subscriptionId": {
            "type": "integer"
          },
          "planId": {
            "type": "string"
          },
          "status": {
            "type": "string",
            "enum": [
              "active"
            ]
          },
          "currentPeriodStart": {
            "type": "string",
            "format": "date-time"
          },
          "currentPeriodEnd": {
            "type": "string",
            "format": "date-time"
          },
          "cancelAtPeriodEnd": {
            "type": "boolean",
            "description": "The subscription ends at the period end in Stripe; Ledger ends the plan then."
          },
          "grantedCredits": {
            "type": "integer",
            "minimum": 0,
            "description": "Credits granted for the current period."
          },
          "imported": {
            "type": "boolean",
            "description": "False when the subscription had already been imported."
          }
        },
        "required": [
          "subscriptionId",
          "planId",
          "status",
          "currentPeriodStart",
          "currentPeriodEnd",
          "cancelAtPeriodEnd",
          "grantedCredits",
          "imported"
        ],
        "additionalProperties": false
      },
      "ImportError": {
        "type": "object",
        "properties": {
          "error": {
            "type": "string"
          },
          "reason": {
            "type": "string",
            "description": "For unsupported_subscription: what Ledger can't bill yet, such as discount or multiple_items."
          },
          "message": {
            "type": "string",
            "description": "Set when Ledger had already moved the subscription to the plan's Stripe Price (no charge) before refusing: what to fix before importing again."
          }
        },
        "required": [
          "error"
        ]
      },
      "ImportSubscriptionInput": {
        "type": "object",
        "properties": {
          "customerId": {
            "type": "string",
            "minLength": 1,
            "maxLength": 255,
            "description": "The Ledger customer. Link its Stripe customer first with POST /v1/customers/stripe-customer."
          },
          "stripeSubscriptionId": {
            "type": "string",
            "maxLength": 255,
            "description": "The customer's existing subscription in your Stripe account."
          },
          "planId": {
            "type": "string",
            "minLength": 1,
            "maxLength": 255,
            "description": "The paid Ledger plan it becomes. Its monthly price must equal the subscription's."
          },
          "startingBalance": {
            "type": "integer",
            "minimum": 0,
            "description": "The plan credits the customer has left this period, to carry over their usage. Defaults to the plan's monthly credits."
          }
        },
        "required": [
          "customerId",
          "stripeSubscriptionId",
          "planId"
        ],
        "additionalProperties": false
      },
      "Downgrade": {
        "type": "object",
        "properties": {
          "downgradeId": {
            "type": "integer"
          },
          "status": {
            "type": "string",
            "enum": [
              "pending",
              "scheduled",
              "applied",
              "closed"
            ]
          },
          "fromPlanId": {
            "type": "string"
          },
          "planId": {
            "type": "string"
          },
          "timing": {
            "type": "string",
            "enum": [
              "period_end",
              "immediately"
            ]
          },
          "refund": {
            "type": "string",
            "enum": [
              "none",
              "prorated"
            ]
          },
          "oldPlanCredits": {
            "type": "string",
            "enum": [
              "keep",
              "expire"
            ]
          },
          "overageOnPlanChange": {
            "type": "string",
            "enum": [
              "carry_to_renewal",
              "bill_now"
            ]
          },
          "effectiveAt": {
            "type": "string",
            "nullable": true,
            "format": "date-time"
          },
          "refundAmountCents": {
            "type": "integer",
            "nullable": true
          },
          "creditNoteId": {
            "type": "string",
            "nullable": true
          }
        },
        "required": [
          "downgradeId",
          "status",
          "fromPlanId",
          "planId",
          "timing",
          "refund",
          "oldPlanCredits",
          "overageOnPlanChange",
          "effectiveAt",
          "refundAmountCents",
          "creditNoteId"
        ]
      },
      "DowngradeInput": {
        "type": "object",
        "properties": {
          "customerId": {
            "type": "string",
            "minLength": 1,
            "maxLength": 255
          },
          "planId": {
            "type": "string",
            "minLength": 1,
            "maxLength": 255
          },
          "timing": {
            "type": "string",
            "enum": [
              "period_end",
              "immediately"
            ],
            "description": "`period_end` (default) switches at the next renewal; `immediately` switches now and keeps the billing period."
          },
          "refund": {
            "type": "string",
            "enum": [
              "none",
              "prorated"
            ],
            "description": "Immediate only. `prorated` refunds (old price − new price) × the unused share of the period as a Stripe credit note on the invoice that paid for it."
          },
          "oldPlanCredits": {
            "type": "string",
            "enum": [
              "keep",
              "expire"
            ],
            "description": "Immediate only. `expire` (default) ends the old plan's credits now; `keep` leaves them until their original expiry."
          },
          "overageOnPlanChange": {
            "type": "string",
            "enum": [
              "carry_to_renewal",
              "bill_now"
            ],
            "description": "Immediate only. When the old plan's unbilled overage is billed, at its own rate: on the next renewal (default) or on an invoice now."
          }
        },
        "required": [
          "customerId",
          "planId"
        ],
        "additionalProperties": false
      },
      "ScheduledDowngrade": {
        "type": "object",
        "properties": {
          "scheduled": {
            "allOf": [
              {
                "$ref": "#/components/schemas/Downgrade"
              },
              {
                "nullable": true
              }
            ]
          }
        },
        "required": [
          "scheduled"
        ]
      },
      "MigrationPreview": {
        "type": "object",
        "properties": {
          "prices": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/MigrationPrice"
            }
          },
          "subscriptions": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/MigrationSubscription"
            }
          },
          "truncated": {
            "type": "boolean",
            "description": "More open subscriptions exist than one preview reads (1,000). Import these, then preview again."
          }
        },
        "required": [
          "prices",
          "subscriptions",
          "truncated"
        ]
      },
      "MigrationPrice": {
        "type": "object",
        "properties": {
          "priceId": {
            "type": "string"
          },
          "productId": {
            "type": "string"
          },
          "productName": {
            "type": "string"
          },
          "unitAmountCents": {
            "type": "integer",
            "nullable": true
          },
          "currency": {
            "type": "string"
          },
          "interval": {
            "type": "string",
            "nullable": true
          },
          "intervalCount": {
            "type": "integer",
            "nullable": true
          },
          "openSubscriptions": {
            "type": "integer",
            "description": "Subscriptions on this Price that Stripe still bills."
          },
          "importableSubscriptions": {
            "type": "integer",
            "description": "Of those, the ones an import would accept and Ledger doesn't hold yet."
          },
          "ledgerPlanId": {
            "type": "string",
            "nullable": true,
            "description": "Set when this Price is the synced Stripe Price of one of your Ledger plans."
          },
          "refusal": {
            "$ref": "#/components/schemas/MigrationRefusal"
          }
        },
        "required": [
          "priceId",
          "productId",
          "productName",
          "unitAmountCents",
          "currency",
          "interval",
          "intervalCount",
          "openSubscriptions",
          "importableSubscriptions",
          "ledgerPlanId",
          "refusal"
        ]
      },
      "MigrationRefusal": {
        "type": "object",
        "nullable": true,
        "properties": {
          "error": {
            "type": "string",
            "description": "The code POST /v1/billing/import would answer, such as unsupported_subscription or multiple_subscriptions."
          },
          "reason": {
            "type": "string",
            "description": "For unsupported_subscription: what Ledger can't bill yet, such as not_monthly, metered_price or discount."
          }
        },
        "required": [
          "error"
        ],
        "description": "Set when no subscription on this Price can be imported: a yearly, metered, free or non-USD Price."
      },
      "MigrationSubscription": {
        "type": "object",
        "properties": {
          "stripeSubscriptionId": {
            "type": "string"
          },
          "stripeCustomerId": {
            "type": "string"
          },
          "customerMetadata": {
            "type": "object",
            "additionalProperties": {
              "type": "string"
            },
            "description": "The Stripe customer's metadata, to choose your own customer ID from."
          },
          "priceId": {
            "type": "string",
            "nullable": true
          },
          "status": {
            "type": "string"
          },
          "currentPeriodEnd": {
            "type": "string",
            "nullable": true,
            "format": "date-time",
            "description": "The next renewal."
          },
          "cancelAtPeriodEnd": {
            "type": "boolean"
          },
          "importedAs": {
            "type": "object",
            "nullable": true,
            "properties": {
              "customerId": {
                "type": "string"
              },
              "planId": {
                "type": "string"
              }
            },
            "required": [
              "customerId",
              "planId"
            ],
            "description": "Set when Ledger already holds it."
          },
          "refusal": {
            "$ref": "#/components/schemas/MigrationRefusal"
          }
        },
        "required": [
          "stripeSubscriptionId",
          "stripeCustomerId",
          "customerMetadata",
          "priceId",
          "status",
          "currentPeriodEnd",
          "cancelAtPeriodEnd",
          "importedAs",
          "refusal"
        ]
      },
      "MigrationError": {
        "type": "object",
        "properties": {
          "error": {
            "type": "string"
          }
        },
        "required": [
          "error"
        ]
      },
      "WebhookEndpointList": {
        "type": "object",
        "properties": {
          "items": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/WebhookEndpoint"
            }
          }
        },
        "required": [
          "items"
        ]
      },
      "WebhookEndpoint": {
        "type": "object",
        "properties": {
          "object": {
            "type": "string",
            "enum": [
              "webhook_endpoint"
            ]
          },
          "id": {
            "type": "string"
          },
          "url": {
            "type": "string"
          },
          "description": {
            "type": "string",
            "nullable": true
          },
          "enabledEvents": {
            "type": "array",
            "items": {
              "type": "string"
            }
          },
          "apiVersion": {
            "type": "string"
          },
          "status": {
            "type": "string",
            "enum": [
              "enabled",
              "disabled"
            ]
          },
          "disabledReason": {
            "type": "string",
            "nullable": true,
            "enum": [
              "failing",
              "manual",
              null
            ]
          },
          "disabledAt": {
            "type": "string",
            "nullable": true
          },
          "failingSince": {
            "type": "string",
            "nullable": true
          },
          "livemode": {
            "type": "boolean"
          },
          "environment": {
            "$ref": "#/components/schemas/WebhookEnvironment"
          },
          "createdAt": {
            "type": "string"
          },
          "updatedAt": {
            "type": "string"
          }
        },
        "required": [
          "object",
          "id",
          "url",
          "description",
          "enabledEvents",
          "apiVersion",
          "status",
          "disabledReason",
          "disabledAt",
          "failingSince",
          "livemode",
          "environment",
          "createdAt",
          "updatedAt"
        ]
      },
      "WebhookEnvironment": {
        "type": "string",
        "enum": [
          "sandbox",
          "live"
        ]
      },
      "WebhookError": {
        "type": "object",
        "properties": {
          "error": {
            "type": "string"
          }
        },
        "required": [
          "error"
        ]
      },
      "CreatedWebhookEndpoint": {
        "allOf": [
          {
            "$ref": "#/components/schemas/WebhookEndpoint"
          },
          {
            "type": "object",
            "properties": {
              "secret": {
                "type": "string",
                "description": "The signing secret (`whsec_…`). Returned only here; store it to verify the Ledger-Signature header."
              }
            },
            "required": [
              "secret"
            ]
          }
        ]
      },
      "CreateWebhookEndpointInput": {
        "type": "object",
        "properties": {
          "url": {
            "type": "string",
            "minLength": 1,
            "maxLength": 2048,
            "description": "Where Ledger sends events. Live and production endpoints must be public `https://` URLs.",
            "example": "https://example.com/webhooks/ledger"
          },
          "enabledEvents": {
            "type": "array",
            "items": {
              "type": "string",
              "enum": [
                "*",
                "customer.created",
                "customer.deleted",
                "subscription.created",
                "subscription.updated",
                "subscription.canceled",
                "invoice.paid",
                "credits.granted",
                "balance.low",
                "balance.depleted",
                "checkout.completed",
                "checkout.expired"
              ],
              "description": "An event type, or `*` for every event."
            },
            "minItems": 1,
            "maxItems": 50
          },
          "description": {
            "type": "string",
            "nullable": true,
            "maxLength": 200
          }
        },
        "required": [
          "url",
          "enabledEvents"
        ]
      },
      "UpdateWebhookEndpointInput": {
        "type": "object",
        "properties": {
          "url": {
            "type": "string",
            "minLength": 1,
            "maxLength": 2048,
            "description": "Where Ledger sends events. Live and production endpoints must be public `https://` URLs.",
            "example": "https://example.com/webhooks/ledger"
          },
          "enabledEvents": {
            "type": "array",
            "items": {
              "type": "string",
              "enum": [
                "*",
                "customer.created",
                "customer.deleted",
                "subscription.created",
                "subscription.updated",
                "subscription.canceled",
                "invoice.paid",
                "credits.granted",
                "balance.low",
                "balance.depleted",
                "checkout.completed",
                "checkout.expired"
              ],
              "description": "An event type, or `*` for every event."
            },
            "minItems": 1,
            "maxItems": 50
          },
          "description": {
            "type": "string",
            "nullable": true,
            "maxLength": 200
          },
          "disabled": {
            "type": "boolean",
            "description": "true turns the endpoint off. false turns it back on, including after Ledger turned it off for failing."
          }
        }
      },
      "DeletedWebhookEndpoint": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string"
          },
          "deleted": {
            "type": "boolean",
            "const": true
          }
        },
        "required": [
          "id",
          "deleted"
        ]
      }
    },
    "parameters": {}
  },
  "paths": {
    "/v1/catalog": {
      "post": {
        "operationId": "publishCatalog",
        "security": [
          {
            "ApiKey": []
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/CatalogInput"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Catalog accepted. changed is false when the Ledger catalog is unchanged. stripeSync reports paid-plan Stripe Product sync after commit.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CatalogPublishResponse"
                }
              }
            }
          },
          "400": {
            "description": "Invalid catalog",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CatalogError"
                }
              }
            }
          },
          "401": {
            "description": "Missing or invalid Ledger API key",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CatalogError"
                }
              }
            }
          },
          "409": {
            "description": "An add-on with a pending Checkout cannot change credits or price",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CatalogError"
                }
              }
            }
          },
          "413": {
            "$ref": "#/components/responses/PayloadTooLarge"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        },
        "summary": "Publish an organization's catalog",
        "x-codeSamples": [
          {
            "lang": "python",
            "label": "Python SDK",
            "source": "import os\nfrom integrateledger import ledger\n\nclient = ledger(api_key=os.environ[\"LEDGER_API_KEY\"])\nclient.publish_catalog({\"schemaVersion\": 1, \"environment\": \"sandbox\", \"currency\": \"usd\", \"plans\": [{\"id\": \"starter\", \"name\": \"Starter\", \"monthlyCredits\": 100, \"monthlyPriceCents\": 1900}], \"features\": [{\"id\": \"api_call\", \"name\": \"API call\", \"creditCost\": 1}], \"addOns\": [], \"policy\": {\"onInsufficientCredits\": \"block\"}})"
          },
          {
            "lang": "bash",
            "label": "cURL",
            "source": "curl --request POST 'https://api.integrateledger.com/v1/catalog' \\\n  -H \"Authorization: Bearer $LEDGER_API_KEY\" \\\n  -H 'Content-Type: application/json' \\\n  --data '{\"schemaVersion\":1,\"environment\":\"sandbox\",\"currency\":\"usd\",\"plans\":[{\"id\":\"starter\",\"name\":\"Starter\",\"monthlyCredits\":100,\"monthlyPriceCents\":1900}],\"features\":[{\"id\":\"api_call\",\"name\":\"API call\",\"creditCost\":1}],\"addOns\":[],\"policy\":{\"onInsufficientCredits\":\"block\"}}'"
          },
          {
            "lang": "typescript",
            "label": "TypeScript SDK",
            "source": "import ledger from \"integrateledger\";\n\nconst client = new ledger({ apiKey: process.env.LEDGER_API_KEY ?? \"\" });\nawait client.publishCatalog({ schemaVersion: 1, environment: \"sandbox\", currency: \"usd\", plans: [{ id: \"starter\", name: \"Starter\", monthlyCredits: 100, monthlyPriceCents: 1900 }], features: [{ id: \"api_call\", name: \"API call\", creditCost: 1 }], addOns: [], policy: { onInsufficientCredits: \"block\" } });"
          }
        ],
        "tags": [
          "Catalog"
        ]
      }
    },
    "/v1/features": {
      "get": {
        "operationId": "listFeatures",
        "security": [
          {
            "ApiKey": []
          }
        ],
        "responses": {
          "200": {
            "description": "Active features",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/FeatureList"
                }
              }
            }
          },
          "401": {
            "description": "Invalid Ledger API key",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "error": {
                      "type": "string"
                    }
                  },
                  "required": [
                    "error"
                  ]
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        },
        "summary": "List active features",
        "x-codeSamples": [
          {
            "lang": "python",
            "label": "Python SDK",
            "source": "import os\nfrom integrateledger import ledger\n\nclient = ledger(api_key=os.environ[\"LEDGER_API_KEY\"])\nclient.list_features()"
          },
          {
            "lang": "bash",
            "label": "cURL",
            "source": "curl --request GET 'https://api.integrateledger.com/v1/features' \\\n  -H \"Authorization: Bearer $LEDGER_API_KEY\""
          },
          {
            "lang": "typescript",
            "label": "TypeScript SDK",
            "source": "import ledger from \"integrateledger\";\n\nconst client = new ledger({ apiKey: process.env.LEDGER_API_KEY ?? \"\" });\nawait client.listFeatures();"
          }
        ],
        "tags": [
          "Features"
        ]
      },
      "post": {
        "operationId": "createFeature",
        "security": [
          {
            "ApiKey": []
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/CreateFeatureInput"
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Feature created",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/FeatureMutation"
                },
                "example": {
                  "item": {
                    "id": "api_call",
                    "name": "API call",
                    "creditCost": 1
                  },
                  "changed": true
                }
              }
            }
          },
          "400": {
            "description": "Invalid request",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "error": {
                      "type": "string"
                    }
                  },
                  "required": [
                    "error"
                  ]
                }
              }
            }
          },
          "401": {
            "description": "Invalid Ledger API key",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "error": {
                      "type": "string"
                    }
                  },
                  "required": [
                    "error"
                  ]
                }
              }
            }
          },
          "409": {
            "description": "Item ID already exists or add-on Checkout is pending",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "error": {
                      "type": "string"
                    }
                  },
                  "required": [
                    "error"
                  ]
                }
              }
            }
          },
          "413": {
            "$ref": "#/components/responses/PayloadTooLarge"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        },
        "summary": "Create a feature",
        "x-codeSamples": [
          {
            "lang": "python",
            "label": "Python SDK",
            "source": "import os\nfrom integrateledger import ledger\n\nclient = ledger(api_key=os.environ[\"LEDGER_API_KEY\"])\nclient.create_feature(id=\"api_call\", name=\"API call\", credit_cost=1)"
          },
          {
            "lang": "bash",
            "label": "cURL",
            "source": "curl --request POST 'https://api.integrateledger.com/v1/features' \\\n  -H \"Authorization: Bearer $LEDGER_API_KEY\" \\\n  -H 'Content-Type: application/json' \\\n  --data '{\"id\":\"api_call\",\"name\":\"API call\",\"creditCost\":1}'"
          },
          {
            "lang": "typescript",
            "label": "TypeScript SDK",
            "source": "import ledger from \"integrateledger\";\n\nconst client = new ledger({ apiKey: process.env.LEDGER_API_KEY ?? \"\" });\nawait client.createFeature({ id: \"api_call\", name: \"API call\", creditCost: 1 });"
          }
        ],
        "tags": [
          "Features"
        ]
      }
    },
    "/v1/features/{id}": {
      "get": {
        "operationId": "getFeature",
        "security": [
          {
            "ApiKey": []
          }
        ],
        "parameters": [
          {
            "schema": {
              "type": "string",
              "minLength": 1,
              "maxLength": 100,
              "pattern": "^[a-zA-Z0-9_-]+$"
            },
            "required": true,
            "name": "id",
            "in": "path"
          }
        ],
        "responses": {
          "200": {
            "description": "Active Feature",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/FeatureItem"
                }
              }
            }
          },
          "400": {
            "description": "Invalid request",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "error": {
                      "type": "string"
                    }
                  },
                  "required": [
                    "error"
                  ]
                }
              }
            }
          },
          "401": {
            "description": "Invalid Ledger API key",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "error": {
                      "type": "string"
                    }
                  },
                  "required": [
                    "error"
                  ]
                }
              }
            }
          },
          "404": {
            "description": "Item not found",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "error": {
                      "type": "string"
                    }
                  },
                  "required": [
                    "error"
                  ]
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        },
        "summary": "Get a feature",
        "x-codeSamples": [
          {
            "lang": "python",
            "label": "Python SDK",
            "source": "import os\nfrom integrateledger import ledger\n\nclient = ledger(api_key=os.environ[\"LEDGER_API_KEY\"])\nclient.get_feature(\"api_call\")"
          },
          {
            "lang": "bash",
            "label": "cURL",
            "source": "curl --request GET 'https://api.integrateledger.com/v1/features/api_call' \\\n  -H \"Authorization: Bearer $LEDGER_API_KEY\""
          },
          {
            "lang": "typescript",
            "label": "TypeScript SDK",
            "source": "import ledger from \"integrateledger\";\n\nconst client = new ledger({ apiKey: process.env.LEDGER_API_KEY ?? \"\" });\nawait client.getFeature({ id: \"api_call\" });"
          }
        ],
        "tags": [
          "Features"
        ]
      },
      "post": {
        "operationId": "updateFeature",
        "security": [
          {
            "ApiKey": []
          }
        ],
        "parameters": [
          {
            "schema": {
              "type": "string",
              "minLength": 1,
              "maxLength": 100,
              "pattern": "^[a-zA-Z0-9_-]+$"
            },
            "required": true,
            "name": "id",
            "in": "path"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/UpdateFeatureInput"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Feature updated",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/FeatureMutation"
                }
              }
            }
          },
          "400": {
            "description": "Invalid request",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "error": {
                      "type": "string"
                    }
                  },
                  "required": [
                    "error"
                  ]
                }
              }
            }
          },
          "401": {
            "description": "Invalid Ledger API key",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "error": {
                      "type": "string"
                    }
                  },
                  "required": [
                    "error"
                  ]
                }
              }
            }
          },
          "404": {
            "description": "Item not found",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "error": {
                      "type": "string"
                    }
                  },
                  "required": [
                    "error"
                  ]
                }
              }
            }
          },
          "409": {
            "description": "Item ID already exists or add-on Checkout is pending",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "error": {
                      "type": "string"
                    }
                  },
                  "required": [
                    "error"
                  ]
                }
              }
            }
          },
          "413": {
            "$ref": "#/components/responses/PayloadTooLarge"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        },
        "summary": "Update a feature",
        "x-codeSamples": [
          {
            "lang": "python",
            "label": "Python SDK",
            "source": "import os\nfrom integrateledger import ledger\n\nclient = ledger(api_key=os.environ[\"LEDGER_API_KEY\"])\nclient.update_feature(\"api_call\", name=\"API call\", credit_cost=2)"
          },
          {
            "lang": "bash",
            "label": "cURL",
            "source": "curl --request POST 'https://api.integrateledger.com/v1/features/api_call' \\\n  -H \"Authorization: Bearer $LEDGER_API_KEY\" \\\n  -H 'Content-Type: application/json' \\\n  --data '{\"name\":\"API call\",\"creditCost\":2}'"
          },
          {
            "lang": "typescript",
            "label": "TypeScript SDK",
            "source": "import ledger from \"integrateledger\";\n\nconst client = new ledger({ apiKey: process.env.LEDGER_API_KEY ?? \"\" });\nawait client.updateFeature({ id: \"api_call\", body: { name: \"API call\", creditCost: 2 } });"
          }
        ],
        "tags": [
          "Features"
        ]
      },
      "delete": {
        "operationId": "archiveFeature",
        "security": [
          {
            "ApiKey": []
          }
        ],
        "parameters": [
          {
            "schema": {
              "type": "string",
              "minLength": 1,
              "maxLength": 100,
              "pattern": "^[a-zA-Z0-9_-]+$"
            },
            "required": true,
            "name": "id",
            "in": "path"
          }
        ],
        "responses": {
          "200": {
            "description": "Feature archived",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/FeatureArchived"
                }
              }
            }
          },
          "400": {
            "description": "Invalid request",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "error": {
                      "type": "string"
                    }
                  },
                  "required": [
                    "error"
                  ]
                }
              }
            }
          },
          "401": {
            "description": "Invalid Ledger API key",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "error": {
                      "type": "string"
                    }
                  },
                  "required": [
                    "error"
                  ]
                }
              }
            }
          },
          "404": {
            "description": "Item not found",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "error": {
                      "type": "string"
                    }
                  },
                  "required": [
                    "error"
                  ]
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        },
        "summary": "Archive a feature",
        "x-codeSamples": [
          {
            "lang": "python",
            "label": "Python SDK",
            "source": "import os\nfrom integrateledger import ledger\n\nclient = ledger(api_key=os.environ[\"LEDGER_API_KEY\"])\nclient.archive_feature(\"api_call\")"
          },
          {
            "lang": "bash",
            "label": "cURL",
            "source": "curl --request DELETE 'https://api.integrateledger.com/v1/features/api_call' \\\n  -H \"Authorization: Bearer $LEDGER_API_KEY\""
          },
          {
            "lang": "typescript",
            "label": "TypeScript SDK",
            "source": "import ledger from \"integrateledger\";\n\nconst client = new ledger({ apiKey: process.env.LEDGER_API_KEY ?? \"\" });\nawait client.archiveFeature({ id: \"api_call\" });"
          }
        ],
        "tags": [
          "Features"
        ]
      }
    },
    "/v1/plans": {
      "get": {
        "operationId": "listPlans",
        "security": [
          {
            "ApiKey": []
          }
        ],
        "responses": {
          "200": {
            "description": "Active plans",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PlanList"
                }
              }
            }
          },
          "401": {
            "description": "Invalid Ledger API key",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "error": {
                      "type": "string"
                    }
                  },
                  "required": [
                    "error"
                  ]
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        },
        "summary": "List active plans",
        "x-codeSamples": [
          {
            "lang": "python",
            "label": "Python SDK",
            "source": "import os\nfrom integrateledger import ledger\n\nclient = ledger(api_key=os.environ[\"LEDGER_API_KEY\"])\nclient.list_plans()"
          },
          {
            "lang": "bash",
            "label": "cURL",
            "source": "curl --request GET 'https://api.integrateledger.com/v1/plans' \\\n  -H \"Authorization: Bearer $LEDGER_API_KEY\""
          },
          {
            "lang": "typescript",
            "label": "TypeScript SDK",
            "source": "import ledger from \"integrateledger\";\n\nconst client = new ledger({ apiKey: process.env.LEDGER_API_KEY ?? \"\" });\nawait client.listPlans();"
          }
        ],
        "tags": [
          "Plans"
        ]
      },
      "post": {
        "operationId": "createPlan",
        "security": [
          {
            "ApiKey": []
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/CreatePlanInput"
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Plan created",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PlanMutation"
                },
                "example": {
                  "item": {
                    "id": "starter",
                    "name": "Starter",
                    "monthlyCredits": 100,
                    "monthlyPriceCents": 1900,
                    "stripeSyncStatus": "synced"
                  },
                  "changed": true,
                  "stripeSync": "synced"
                }
              }
            }
          },
          "400": {
            "description": "Invalid request",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "error": {
                      "type": "string"
                    }
                  },
                  "required": [
                    "error"
                  ]
                }
              }
            }
          },
          "401": {
            "description": "Invalid Ledger API key",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "error": {
                      "type": "string"
                    }
                  },
                  "required": [
                    "error"
                  ]
                }
              }
            }
          },
          "409": {
            "description": "Item ID already exists or add-on Checkout is pending",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "error": {
                      "type": "string"
                    }
                  },
                  "required": [
                    "error"
                  ]
                }
              }
            }
          },
          "413": {
            "$ref": "#/components/responses/PayloadTooLarge"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        },
        "summary": "Create a plan",
        "x-codeSamples": [
          {
            "lang": "python",
            "label": "Python SDK",
            "source": "import os\nfrom integrateledger import ledger\n\nclient = ledger(api_key=os.environ[\"LEDGER_API_KEY\"])\nclient.create_plan(id=\"starter\", name=\"Starter\", monthly_credits=100, monthly_price_cents=1900)"
          },
          {
            "lang": "bash",
            "label": "cURL",
            "source": "curl --request POST 'https://api.integrateledger.com/v1/plans' \\\n  -H \"Authorization: Bearer $LEDGER_API_KEY\" \\\n  -H 'Content-Type: application/json' \\\n  --data '{\"id\":\"starter\",\"name\":\"Starter\",\"monthlyCredits\":100,\"monthlyPriceCents\":1900}'"
          },
          {
            "lang": "typescript",
            "label": "TypeScript SDK",
            "source": "import ledger from \"integrateledger\";\n\nconst client = new ledger({ apiKey: process.env.LEDGER_API_KEY ?? \"\" });\nawait client.createPlan({ id: \"starter\", name: \"Starter\", monthlyCredits: 100, monthlyPriceCents: 1900 });"
          }
        ],
        "tags": [
          "Plans"
        ]
      }
    },
    "/v1/plans/{id}": {
      "get": {
        "operationId": "getPlan",
        "security": [
          {
            "ApiKey": []
          }
        ],
        "parameters": [
          {
            "schema": {
              "type": "string",
              "minLength": 1,
              "maxLength": 100,
              "pattern": "^[a-zA-Z0-9_-]+$"
            },
            "required": true,
            "name": "id",
            "in": "path"
          }
        ],
        "responses": {
          "200": {
            "description": "Active Plan",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PlanItem"
                }
              }
            }
          },
          "400": {
            "description": "Invalid request",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "error": {
                      "type": "string"
                    }
                  },
                  "required": [
                    "error"
                  ]
                }
              }
            }
          },
          "401": {
            "description": "Invalid Ledger API key",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "error": {
                      "type": "string"
                    }
                  },
                  "required": [
                    "error"
                  ]
                }
              }
            }
          },
          "404": {
            "description": "Item not found",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "error": {
                      "type": "string"
                    }
                  },
                  "required": [
                    "error"
                  ]
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        },
        "summary": "Get a plan",
        "x-codeSamples": [
          {
            "lang": "python",
            "label": "Python SDK",
            "source": "import os\nfrom integrateledger import ledger\n\nclient = ledger(api_key=os.environ[\"LEDGER_API_KEY\"])\nclient.get_plan(\"starter\")"
          },
          {
            "lang": "bash",
            "label": "cURL",
            "source": "curl --request GET 'https://api.integrateledger.com/v1/plans/starter' \\\n  -H \"Authorization: Bearer $LEDGER_API_KEY\""
          },
          {
            "lang": "typescript",
            "label": "TypeScript SDK",
            "source": "import ledger from \"integrateledger\";\n\nconst client = new ledger({ apiKey: process.env.LEDGER_API_KEY ?? \"\" });\nawait client.getPlan({ id: \"starter\" });"
          }
        ],
        "tags": [
          "Plans"
        ]
      },
      "post": {
        "operationId": "updatePlan",
        "security": [
          {
            "ApiKey": []
          }
        ],
        "parameters": [
          {
            "schema": {
              "type": "string",
              "minLength": 1,
              "maxLength": 100,
              "pattern": "^[a-zA-Z0-9_-]+$"
            },
            "required": true,
            "name": "id",
            "in": "path"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/UpdatePlanInput"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Plan updated",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PlanMutation"
                }
              }
            }
          },
          "400": {
            "description": "Invalid request",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "error": {
                      "type": "string"
                    }
                  },
                  "required": [
                    "error"
                  ]
                }
              }
            }
          },
          "401": {
            "description": "Invalid Ledger API key",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "error": {
                      "type": "string"
                    }
                  },
                  "required": [
                    "error"
                  ]
                }
              }
            }
          },
          "404": {
            "description": "Item not found",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "error": {
                      "type": "string"
                    }
                  },
                  "required": [
                    "error"
                  ]
                }
              }
            }
          },
          "409": {
            "description": "Item ID already exists or add-on Checkout is pending",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "error": {
                      "type": "string"
                    }
                  },
                  "required": [
                    "error"
                  ]
                }
              }
            }
          },
          "413": {
            "$ref": "#/components/responses/PayloadTooLarge"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        },
        "summary": "Update a plan",
        "x-codeSamples": [
          {
            "lang": "python",
            "label": "Python SDK",
            "source": "import os\nfrom integrateledger import ledger\n\nclient = ledger(api_key=os.environ[\"LEDGER_API_KEY\"])\nclient.update_plan(\"starter\", name=\"Starter\", monthly_credits=200, monthly_price_cents=2900)"
          },
          {
            "lang": "bash",
            "label": "cURL",
            "source": "curl --request POST 'https://api.integrateledger.com/v1/plans/starter' \\\n  -H \"Authorization: Bearer $LEDGER_API_KEY\" \\\n  -H 'Content-Type: application/json' \\\n  --data '{\"name\":\"Starter\",\"monthlyCredits\":200,\"monthlyPriceCents\":2900}'"
          },
          {
            "lang": "typescript",
            "label": "TypeScript SDK",
            "source": "import ledger from \"integrateledger\";\n\nconst client = new ledger({ apiKey: process.env.LEDGER_API_KEY ?? \"\" });\nawait client.updatePlan({ id: \"starter\", body: { name: \"Starter\", monthlyCredits: 200, monthlyPriceCents: 2900 } });"
          }
        ],
        "tags": [
          "Plans"
        ]
      },
      "delete": {
        "operationId": "archivePlan",
        "security": [
          {
            "ApiKey": []
          }
        ],
        "parameters": [
          {
            "schema": {
              "type": "string",
              "minLength": 1,
              "maxLength": 100,
              "pattern": "^[a-zA-Z0-9_-]+$"
            },
            "required": true,
            "name": "id",
            "in": "path"
          }
        ],
        "responses": {
          "200": {
            "description": "Plan archived",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PlanArchived"
                }
              }
            }
          },
          "400": {
            "description": "Invalid request",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "error": {
                      "type": "string"
                    }
                  },
                  "required": [
                    "error"
                  ]
                }
              }
            }
          },
          "401": {
            "description": "Invalid Ledger API key",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "error": {
                      "type": "string"
                    }
                  },
                  "required": [
                    "error"
                  ]
                }
              }
            }
          },
          "404": {
            "description": "Item not found",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "error": {
                      "type": "string"
                    }
                  },
                  "required": [
                    "error"
                  ]
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        },
        "summary": "Archive a plan",
        "x-codeSamples": [
          {
            "lang": "python",
            "label": "Python SDK",
            "source": "import os\nfrom integrateledger import ledger\n\nclient = ledger(api_key=os.environ[\"LEDGER_API_KEY\"])\nclient.archive_plan(\"starter\")"
          },
          {
            "lang": "bash",
            "label": "cURL",
            "source": "curl --request DELETE 'https://api.integrateledger.com/v1/plans/starter' \\\n  -H \"Authorization: Bearer $LEDGER_API_KEY\""
          },
          {
            "lang": "typescript",
            "label": "TypeScript SDK",
            "source": "import ledger from \"integrateledger\";\n\nconst client = new ledger({ apiKey: process.env.LEDGER_API_KEY ?? \"\" });\nawait client.archivePlan({ id: \"starter\" });"
          }
        ],
        "tags": [
          "Plans"
        ]
      }
    },
    "/v1/add-ons": {
      "get": {
        "operationId": "listAddOns",
        "security": [
          {
            "ApiKey": []
          }
        ],
        "responses": {
          "200": {
            "description": "Active add-ons",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/AddOnList"
                }
              }
            }
          },
          "401": {
            "description": "Invalid Ledger API key",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "error": {
                      "type": "string"
                    }
                  },
                  "required": [
                    "error"
                  ]
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        },
        "summary": "List active add-ons",
        "x-codeSamples": [
          {
            "lang": "python",
            "label": "Python SDK",
            "source": "import os\nfrom integrateledger import ledger\n\nclient = ledger(api_key=os.environ[\"LEDGER_API_KEY\"])\nclient.list_add_ons()"
          },
          {
            "lang": "bash",
            "label": "cURL",
            "source": "curl --request GET 'https://api.integrateledger.com/v1/add-ons' \\\n  -H \"Authorization: Bearer $LEDGER_API_KEY\""
          },
          {
            "lang": "typescript",
            "label": "TypeScript SDK",
            "source": "import ledger from \"integrateledger\";\n\nconst client = new ledger({ apiKey: process.env.LEDGER_API_KEY ?? \"\" });\nawait client.listAddOns();"
          }
        ],
        "tags": [
          "Add-ons"
        ]
      },
      "post": {
        "operationId": "createAddOn",
        "security": [
          {
            "ApiKey": []
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/CreateAddOnInput"
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "AddOn created",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/AddOnMutation"
                },
                "example": {
                  "item": {
                    "id": "extra_credits",
                    "name": "Extra credits",
                    "type": "credit_pack",
                    "creditAmount": 50,
                    "priceCents": 500
                  },
                  "changed": true
                }
              }
            }
          },
          "400": {
            "description": "Invalid request",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "error": {
                      "type": "string"
                    }
                  },
                  "required": [
                    "error"
                  ]
                }
              }
            }
          },
          "401": {
            "description": "Invalid Ledger API key",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "error": {
                      "type": "string"
                    }
                  },
                  "required": [
                    "error"
                  ]
                }
              }
            }
          },
          "409": {
            "description": "Item ID already exists or add-on Checkout is pending",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "error": {
                      "type": "string"
                    }
                  },
                  "required": [
                    "error"
                  ]
                }
              }
            }
          },
          "413": {
            "$ref": "#/components/responses/PayloadTooLarge"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        },
        "summary": "Create an add-on",
        "x-codeSamples": [
          {
            "lang": "python",
            "label": "Python SDK",
            "source": "import os\nfrom integrateledger import ledger\n\nclient = ledger(api_key=os.environ[\"LEDGER_API_KEY\"])\nclient.create_add_on(id=\"extra_credits\", name=\"Extra credits\", credit_amount=50, price_cents=500)"
          },
          {
            "lang": "bash",
            "label": "cURL",
            "source": "curl --request POST 'https://api.integrateledger.com/v1/add-ons' \\\n  -H \"Authorization: Bearer $LEDGER_API_KEY\" \\\n  -H 'Content-Type: application/json' \\\n  --data '{\"id\":\"extra_credits\",\"name\":\"Extra credits\",\"type\":\"credit_pack\",\"creditAmount\":50,\"priceCents\":500}'"
          },
          {
            "lang": "typescript",
            "label": "TypeScript SDK",
            "source": "import ledger from \"integrateledger\";\n\nconst client = new ledger({ apiKey: process.env.LEDGER_API_KEY ?? \"\" });\nawait client.createAddOn({ id: \"extra_credits\", name: \"Extra credits\", type: \"credit_pack\", creditAmount: 50, priceCents: 500 });"
          }
        ],
        "tags": [
          "Add-ons"
        ]
      }
    },
    "/v1/add-ons/{id}": {
      "get": {
        "operationId": "getAddOn",
        "security": [
          {
            "ApiKey": []
          }
        ],
        "parameters": [
          {
            "schema": {
              "type": "string",
              "minLength": 1,
              "maxLength": 100,
              "pattern": "^[a-zA-Z0-9_-]+$"
            },
            "required": true,
            "name": "id",
            "in": "path"
          }
        ],
        "responses": {
          "200": {
            "description": "Active AddOn",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/AddOnItem"
                }
              }
            }
          },
          "400": {
            "description": "Invalid request",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "error": {
                      "type": "string"
                    }
                  },
                  "required": [
                    "error"
                  ]
                }
              }
            }
          },
          "401": {
            "description": "Invalid Ledger API key",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "error": {
                      "type": "string"
                    }
                  },
                  "required": [
                    "error"
                  ]
                }
              }
            }
          },
          "404": {
            "description": "Item not found",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "error": {
                      "type": "string"
                    }
                  },
                  "required": [
                    "error"
                  ]
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        },
        "summary": "Get an add-on",
        "x-codeSamples": [
          {
            "lang": "python",
            "label": "Python SDK",
            "source": "import os\nfrom integrateledger import ledger\n\nclient = ledger(api_key=os.environ[\"LEDGER_API_KEY\"])\nclient.get_add_on(\"extra_credits\")"
          },
          {
            "lang": "bash",
            "label": "cURL",
            "source": "curl --request GET 'https://api.integrateledger.com/v1/add-ons/extra_credits' \\\n  -H \"Authorization: Bearer $LEDGER_API_KEY\""
          },
          {
            "lang": "typescript",
            "label": "TypeScript SDK",
            "source": "import ledger from \"integrateledger\";\n\nconst client = new ledger({ apiKey: process.env.LEDGER_API_KEY ?? \"\" });\nawait client.getAddOn({ id: \"extra_credits\" });"
          }
        ],
        "tags": [
          "Add-ons"
        ]
      },
      "post": {
        "operationId": "updateAddOn",
        "security": [
          {
            "ApiKey": []
          }
        ],
        "parameters": [
          {
            "schema": {
              "type": "string",
              "minLength": 1,
              "maxLength": 100,
              "pattern": "^[a-zA-Z0-9_-]+$"
            },
            "required": true,
            "name": "id",
            "in": "path"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/UpdateAddOnInput"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "AddOn updated",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/AddOnMutation"
                }
              }
            }
          },
          "400": {
            "description": "Invalid request",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "error": {
                      "type": "string"
                    }
                  },
                  "required": [
                    "error"
                  ]
                }
              }
            }
          },
          "401": {
            "description": "Invalid Ledger API key",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "error": {
                      "type": "string"
                    }
                  },
                  "required": [
                    "error"
                  ]
                }
              }
            }
          },
          "404": {
            "description": "Item not found",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "error": {
                      "type": "string"
                    }
                  },
                  "required": [
                    "error"
                  ]
                }
              }
            }
          },
          "409": {
            "description": "Item ID already exists or add-on Checkout is pending",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "error": {
                      "type": "string"
                    }
                  },
                  "required": [
                    "error"
                  ]
                }
              }
            }
          },
          "413": {
            "$ref": "#/components/responses/PayloadTooLarge"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        },
        "summary": "Update an add-on",
        "x-codeSamples": [
          {
            "lang": "python",
            "label": "Python SDK",
            "source": "import os\nfrom integrateledger import ledger\n\nclient = ledger(api_key=os.environ[\"LEDGER_API_KEY\"])\nclient.update_add_on(\"extra_credits\", name=\"Extra credits\", credit_amount=100, price_cents=900)"
          },
          {
            "lang": "bash",
            "label": "cURL",
            "source": "curl --request POST 'https://api.integrateledger.com/v1/add-ons/extra_credits' \\\n  -H \"Authorization: Bearer $LEDGER_API_KEY\" \\\n  -H 'Content-Type: application/json' \\\n  --data '{\"name\":\"Extra credits\",\"type\":\"credit_pack\",\"creditAmount\":100,\"priceCents\":900}'"
          },
          {
            "lang": "typescript",
            "label": "TypeScript SDK",
            "source": "import ledger from \"integrateledger\";\n\nconst client = new ledger({ apiKey: process.env.LEDGER_API_KEY ?? \"\" });\nawait client.updateAddOn({ id: \"extra_credits\", body: { name: \"Extra credits\", type: \"credit_pack\", creditAmount: 100, priceCents: 900 } });"
          }
        ],
        "tags": [
          "Add-ons"
        ]
      },
      "delete": {
        "operationId": "archiveAddOn",
        "security": [
          {
            "ApiKey": []
          }
        ],
        "parameters": [
          {
            "schema": {
              "type": "string",
              "minLength": 1,
              "maxLength": 100,
              "pattern": "^[a-zA-Z0-9_-]+$"
            },
            "required": true,
            "name": "id",
            "in": "path"
          }
        ],
        "responses": {
          "200": {
            "description": "AddOn archived",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/AddOnArchived"
                }
              }
            }
          },
          "400": {
            "description": "Invalid request",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "error": {
                      "type": "string"
                    }
                  },
                  "required": [
                    "error"
                  ]
                }
              }
            }
          },
          "401": {
            "description": "Invalid Ledger API key",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "error": {
                      "type": "string"
                    }
                  },
                  "required": [
                    "error"
                  ]
                }
              }
            }
          },
          "404": {
            "description": "Item not found",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "error": {
                      "type": "string"
                    }
                  },
                  "required": [
                    "error"
                  ]
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        },
        "summary": "Archive an add-on",
        "x-codeSamples": [
          {
            "lang": "python",
            "label": "Python SDK",
            "source": "import os\nfrom integrateledger import ledger\n\nclient = ledger(api_key=os.environ[\"LEDGER_API_KEY\"])\nclient.archive_add_on(\"extra_credits\")"
          },
          {
            "lang": "bash",
            "label": "cURL",
            "source": "curl --request DELETE 'https://api.integrateledger.com/v1/add-ons/extra_credits' \\\n  -H \"Authorization: Bearer $LEDGER_API_KEY\""
          },
          {
            "lang": "typescript",
            "label": "TypeScript SDK",
            "source": "import ledger from \"integrateledger\";\n\nconst client = new ledger({ apiKey: process.env.LEDGER_API_KEY ?? \"\" });\nawait client.archiveAddOn({ id: \"extra_credits\" });"
          }
        ],
        "tags": [
          "Add-ons"
        ]
      }
    },
    "/v1/check": {
      "post": {
        "operationId": "checkCredits",
        "security": [
          {
            "ApiKey": []
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/CheckInput"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Current credit availability",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CheckResponse"
                }
              }
            }
          },
          "400": {
            "description": "Invalid input",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CheckError"
                }
              }
            }
          },
          "401": {
            "description": "Invalid Ledger API key",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CheckError"
                }
              }
            }
          },
          "404": {
            "description": "Customer or feature not found",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CheckError"
                }
              }
            }
          },
          "413": {
            "$ref": "#/components/responses/PayloadTooLarge"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        },
        "summary": "Check a customer's available credits",
        "x-codeSamples": [
          {
            "lang": "python",
            "label": "Python SDK",
            "source": "import os\nfrom integrateledger import ledger\n\nclient = ledger(api_key=os.environ[\"LEDGER_API_KEY\"])\nclient.check_credits(customer_id=\"user_123\", feature_id=\"api_call\")"
          },
          {
            "lang": "bash",
            "label": "cURL",
            "source": "curl --request POST 'https://api.integrateledger.com/v1/check' \\\n  -H \"Authorization: Bearer $LEDGER_API_KEY\" \\\n  -H 'Content-Type: application/json' \\\n  --data '{\"customerId\":\"user_123\",\"featureId\":\"api_call\"}'"
          },
          {
            "lang": "typescript",
            "label": "TypeScript SDK",
            "source": "import ledger from \"integrateledger\";\n\nconst client = new ledger({ apiKey: process.env.LEDGER_API_KEY ?? \"\" });\nawait client.checkCredits({ customerId: \"user_123\", featureId: \"api_call\" });"
          }
        ],
        "tags": [
          "Usage"
        ]
      }
    },
    "/v1/customers/subscriptions": {
      "get": {
        "operationId": "listCustomerSubscriptions",
        "security": [
          {
            "ApiKey": []
          }
        ],
        "parameters": [
          {
            "schema": {
              "type": "string",
              "minLength": 1,
              "maxLength": 255
            },
            "required": true,
            "name": "customerId",
            "in": "query"
          },
          {
            "schema": {
              "type": "integer",
              "minimum": 1,
              "maximum": 100
            },
            "required": false,
            "name": "limit",
            "in": "query"
          },
          {
            "schema": {
              "type": "integer",
              "exclusiveMinimum": 0,
              "maximum": 2147483647
            },
            "required": false,
            "name": "cursor",
            "in": "query"
          }
        ],
        "responses": {
          "200": {
            "description": "Customer subscriptions, newest first",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CustomerSubscriptionPage"
                }
              }
            }
          },
          "400": {
            "description": "Invalid query",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CustomerError"
                }
              }
            }
          },
          "401": {
            "description": "Missing or invalid Ledger API key",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CustomerError"
                }
              }
            }
          },
          "404": {
            "description": "Customer not found",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CustomerError"
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        },
        "summary": "List a customer's subscriptions",
        "x-codeSamples": [
          {
            "lang": "python",
            "label": "Python SDK",
            "source": "import os\nfrom integrateledger import ledger\n\nclient = ledger(api_key=os.environ[\"LEDGER_API_KEY\"])\nclient.list_customer_subscriptions(customer_id=\"user_123\")"
          },
          {
            "lang": "bash",
            "label": "cURL",
            "source": "curl --request GET 'https://api.integrateledger.com/v1/customers/subscriptions?customerId=user_123' \\\n  -H \"Authorization: Bearer $LEDGER_API_KEY\""
          },
          {
            "lang": "typescript",
            "label": "TypeScript SDK",
            "source": "import ledger from \"integrateledger\";\n\nconst client = new ledger({ apiKey: process.env.LEDGER_API_KEY ?? \"\" });\nawait client.listCustomerSubscriptions({ customerId: \"user_123\" });"
          }
        ],
        "tags": [
          "Customers"
        ]
      }
    },
    "/v1/customers/purchases": {
      "get": {
        "operationId": "listCustomerPurchases",
        "security": [
          {
            "ApiKey": []
          }
        ],
        "parameters": [
          {
            "schema": {
              "type": "string",
              "minLength": 1,
              "maxLength": 255
            },
            "required": true,
            "name": "customerId",
            "in": "query"
          },
          {
            "schema": {
              "type": "integer",
              "minimum": 1,
              "maximum": 100
            },
            "required": false,
            "name": "limit",
            "in": "query"
          },
          {
            "schema": {
              "type": "integer",
              "exclusiveMinimum": 0,
              "maximum": 2147483647
            },
            "required": false,
            "name": "cursor",
            "in": "query"
          }
        ],
        "responses": {
          "200": {
            "description": "Customer credit-pack purchases, newest first",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CustomerPurchasePage"
                }
              }
            }
          },
          "400": {
            "description": "Invalid query",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CustomerError"
                }
              }
            }
          },
          "401": {
            "description": "Missing or invalid Ledger API key",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CustomerError"
                }
              }
            }
          },
          "404": {
            "description": "Customer not found",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CustomerError"
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        },
        "summary": "List a customer's credit-pack purchases",
        "x-codeSamples": [
          {
            "lang": "python",
            "label": "Python SDK",
            "source": "import os\nfrom integrateledger import ledger\n\nclient = ledger(api_key=os.environ[\"LEDGER_API_KEY\"])\nclient.list_customer_purchases(customer_id=\"user_123\")"
          },
          {
            "lang": "bash",
            "label": "cURL",
            "source": "curl --request GET 'https://api.integrateledger.com/v1/customers/purchases?customerId=user_123' \\\n  -H \"Authorization: Bearer $LEDGER_API_KEY\""
          },
          {
            "lang": "typescript",
            "label": "TypeScript SDK",
            "source": "import ledger from \"integrateledger\";\n\nconst client = new ledger({ apiKey: process.env.LEDGER_API_KEY ?? \"\" });\nawait client.listCustomerPurchases({ customerId: \"user_123\" });"
          }
        ],
        "tags": [
          "Customers"
        ]
      }
    },
    "/v1/customers/subscriptions/{subscriptionId}/cancellation": {
      "post": {
        "operationId": "setSubscriptionCancellation",
        "security": [
          {
            "ApiKey": []
          }
        ],
        "parameters": [
          {
            "schema": {
              "type": "integer",
              "exclusiveMinimum": 0,
              "maximum": 2147483647
            },
            "required": true,
            "name": "subscriptionId",
            "in": "path"
          },
          {
            "schema": {
              "type": "string",
              "minLength": 1,
              "maxLength": 255
            },
            "required": true,
            "name": "customerId",
            "in": "query"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/UpdateCustomerSubscriptionCancellationInput"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Paid subscription renewal setting",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/UpdateCustomerSubscriptionCancellationResponse"
                }
              }
            }
          },
          "400": {
            "description": "Invalid request",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CustomerError"
                }
              }
            }
          },
          "401": {
            "description": "Missing or invalid Ledger API key",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CustomerError"
                }
              }
            }
          },
          "404": {
            "description": "Subscription not found for this customer",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CustomerError"
                }
              }
            }
          },
          "409": {
            "description": "Subscription cannot safely change renewal",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CustomerError"
                }
              }
            }
          },
          "413": {
            "$ref": "#/components/responses/PayloadTooLarge"
          },
          "422": {
            "description": "Organization Stripe connection unavailable",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CustomerError"
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "502": {
            "description": "Stripe unavailable",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CustomerError"
                }
              }
            }
          }
        },
        "summary": "Stop or resume a paid subscription's renewal",
        "x-codeSamples": [
          {
            "lang": "python",
            "label": "Python SDK",
            "source": "import os\nfrom integrateledger import ledger\n\nclient = ledger(api_key=os.environ[\"LEDGER_API_KEY\"])\nclient.set_subscription_cancellation(customer_id=\"user_123\", subscription_id=42, cancel_at_period_end=True)"
          },
          {
            "lang": "bash",
            "label": "cURL",
            "source": "curl --request POST 'https://api.integrateledger.com/v1/customers/subscriptions/42/cancellation?customerId=user_123' \\\n  -H \"Authorization: Bearer $LEDGER_API_KEY\" \\\n  -H 'Content-Type: application/json' \\\n  --data '{\"cancelAtPeriodEnd\":true}'"
          },
          {
            "lang": "typescript",
            "label": "TypeScript SDK",
            "source": "import ledger from \"integrateledger\";\n\nconst client = new ledger({ apiKey: process.env.LEDGER_API_KEY ?? \"\" });\nawait client.setSubscriptionCancellation({ subscriptionId: 42, customerId: \"user_123\", body: { cancelAtPeriodEnd: true } });"
          }
        ],
        "tags": [
          "Customers"
        ]
      }
    },
    "/v1/customers": {
      "delete": {
        "operationId": "deleteCustomer",
        "security": [
          {
            "ApiKey": []
          }
        ],
        "parameters": [
          {
            "schema": {
              "type": "string",
              "minLength": 1,
              "maxLength": 255
            },
            "required": true,
            "name": "customerId",
            "in": "query"
          },
          {
            "schema": {
              "type": "boolean",
              "description": "Also delete the customer's Stripe customer, which cancels its Stripe subscriptions. Required when it has one that isn't canceled."
            },
            "required": false,
            "description": "Also delete the customer's Stripe customer, which cancels its Stripe subscriptions. Required when it has one that isn't canceled.",
            "name": "deleteInStripe",
            "in": "query"
          }
        ],
        "responses": {
          "200": {
            "description": "Customer deleted",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CustomerDeleted"
                }
              }
            }
          },
          "400": {
            "description": "Invalid customer ID",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CustomerError"
                }
              }
            }
          },
          "401": {
            "description": "Missing or invalid Ledger API key",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CustomerError"
                }
              }
            }
          },
          "404": {
            "description": "Customer not found",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CustomerError"
                }
              }
            }
          },
          "409": {
            "description": "The customer has a paid Stripe subscription that isn't canceled; pass deleteInStripe=true",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CustomerError"
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "502": {
            "description": "deleteInStripe was set, but Stripe isn't connected",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CustomerError"
                }
              }
            }
          }
        },
        "summary": "Delete a customer",
        "x-codeSamples": [
          {
            "lang": "python",
            "label": "Python SDK",
            "source": "import os\nfrom integrateledger import ledger\n\nclient = ledger(api_key=os.environ[\"LEDGER_API_KEY\"])\nclient.delete_customer(customer_id=\"user_123\", delete_in_stripe=True)"
          },
          {
            "lang": "bash",
            "label": "cURL",
            "source": "curl --request DELETE 'https://api.integrateledger.com/v1/customers?customerId=user_123&deleteInStripe=true' \\\n  -H \"Authorization: Bearer $LEDGER_API_KEY\""
          },
          {
            "lang": "typescript",
            "label": "TypeScript SDK",
            "source": "import ledger from \"integrateledger\";\n\nconst client = new ledger({ apiKey: process.env.LEDGER_API_KEY ?? \"\" });\nawait client.deleteCustomer({ customerId: \"user_123\", deleteInStripe: true });"
          }
        ],
        "description": "Leave out deleteInStripe to keep the Stripe customer; a customer with an uncanceled paid subscription then returns 409.",
        "tags": [
          "Customers"
        ]
      },
      "get": {
        "operationId": "getCustomer",
        "security": [
          {
            "ApiKey": []
          }
        ],
        "parameters": [
          {
            "schema": {
              "type": "string",
              "minLength": 1,
              "maxLength": 255
            },
            "required": true,
            "name": "customerId",
            "in": "query"
          }
        ],
        "responses": {
          "200": {
            "description": "Customer balance and current plan",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CustomerDetail"
                }
              }
            }
          },
          "400": {
            "description": "Invalid customer ID",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CustomerError"
                }
              }
            }
          },
          "401": {
            "description": "Missing or invalid Ledger API key",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CustomerError"
                }
              }
            }
          },
          "404": {
            "description": "Customer not found",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CustomerError"
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        },
        "summary": "Get a customer and current balance",
        "x-codeSamples": [
          {
            "lang": "python",
            "label": "Python SDK",
            "source": "import os\nfrom integrateledger import ledger\n\nclient = ledger(api_key=os.environ[\"LEDGER_API_KEY\"])\nclient.get_customer(customer_id=\"user_123\")"
          },
          {
            "lang": "bash",
            "label": "cURL",
            "source": "curl --request GET 'https://api.integrateledger.com/v1/customers?customerId=user_123' \\\n  -H \"Authorization: Bearer $LEDGER_API_KEY\""
          },
          {
            "lang": "typescript",
            "label": "TypeScript SDK",
            "source": "import ledger from \"integrateledger\";\n\nconst client = new ledger({ apiKey: process.env.LEDGER_API_KEY ?? \"\" });\nawait client.getCustomer({ customerId: \"user_123\" });"
          }
        ],
        "tags": [
          "Customers"
        ]
      },
      "post": {
        "operationId": "createOrGetCustomer",
        "security": [
          {
            "ApiKey": []
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/CreateCustomerInput"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Customer created or returned without granting credits",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CustomerResponse"
                }
              }
            }
          },
          "400": {
            "description": "Invalid customer ID",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CustomerError"
                }
              }
            }
          },
          "401": {
            "description": "Missing or invalid Ledger API key",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CustomerError"
                }
              }
            }
          },
          "413": {
            "$ref": "#/components/responses/PayloadTooLarge"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        },
        "summary": "Create or get a customer",
        "x-codeSamples": [
          {
            "lang": "python",
            "label": "Python SDK",
            "source": "import os\nfrom integrateledger import ledger\n\nclient = ledger(api_key=os.environ[\"LEDGER_API_KEY\"])\nclient.create_or_get_customer(customer_id=\"user_123\")"
          },
          {
            "lang": "bash",
            "label": "cURL",
            "source": "curl --request POST 'https://api.integrateledger.com/v1/customers' \\\n  -H \"Authorization: Bearer $LEDGER_API_KEY\" \\\n  -H 'Content-Type: application/json' \\\n  --data '{\"customerId\":\"user_123\"}'"
          },
          {
            "lang": "typescript",
            "label": "TypeScript SDK",
            "source": "import ledger from \"integrateledger\";\n\nconst client = new ledger({ apiKey: process.env.LEDGER_API_KEY ?? \"\" });\nawait client.createOrGetCustomer({ customerId: \"user_123\" });"
          }
        ],
        "tags": [
          "Customers"
        ]
      }
    },
    "/v1/customers/overage-cap": {
      "post": {
        "operationId": "setCustomerOverageCap",
        "security": [
          {
            "ApiKey": []
          }
        ],
        "parameters": [
          {
            "schema": {
              "type": "string",
              "minLength": 1,
              "maxLength": 255
            },
            "required": true,
            "name": "customerId",
            "in": "query"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/SetCustomerOverageCapInput"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Customer overage cap saved",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CustomerOverageCap"
                }
              }
            }
          },
          "400": {
            "description": "Invalid request",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CustomerError"
                }
              }
            }
          },
          "401": {
            "description": "Missing or invalid Ledger API key",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CustomerError"
                }
              }
            }
          },
          "404": {
            "description": "Customer not found",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CustomerError"
                }
              }
            }
          },
          "413": {
            "$ref": "#/components/responses/PayloadTooLarge"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        },
        "summary": "Set or clear a customer's overage cap",
        "x-codeSamples": [
          {
            "lang": "python",
            "label": "Python SDK",
            "source": "import os\nfrom integrateledger import ledger\n\nclient = ledger(api_key=os.environ[\"LEDGER_API_KEY\"])\nclient.set_customer_overage_cap(customer_id=\"user_123\", cap_cents=5000)"
          },
          {
            "lang": "bash",
            "label": "cURL",
            "source": "curl --request POST 'https://api.integrateledger.com/v1/customers/overage-cap?customerId=user_123' \\\n  -H \"Authorization: Bearer $LEDGER_API_KEY\" \\\n  -H 'Content-Type: application/json' \\\n  --data '{\"capCents\":5000}'"
          },
          {
            "lang": "typescript",
            "label": "TypeScript SDK",
            "source": "import ledger from \"integrateledger\";\n\nconst client = new ledger({ apiKey: process.env.LEDGER_API_KEY ?? \"\" });\nawait client.setCustomerOverageCap({ customerId: \"user_123\", body: { capCents: 5000 } });"
          }
        ],
        "description": "Send capCents: null to clear the customer's cap and restore the plan default.",
        "tags": [
          "Customers"
        ]
      }
    },
    "/v1/customers/overage-override": {
      "post": {
        "operationId": "setCustomerOverageOverride",
        "security": [
          {
            "ApiKey": []
          }
        ],
        "parameters": [
          {
            "schema": {
              "type": "string",
              "minLength": 1,
              "maxLength": 255
            },
            "required": true,
            "name": "customerId",
            "in": "query"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/SetCustomerOverageOverrideInput"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Customer overage override saved or cleared",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CustomerOverageOverrideResult"
                }
              }
            }
          },
          "400": {
            "description": "Invalid request",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CustomerError"
                }
              }
            }
          },
          "401": {
            "description": "Missing or invalid Ledger API key, or a key without customers:write",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CustomerError"
                }
              }
            }
          },
          "404": {
            "description": "Customer or plan not found",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CustomerError"
                }
              }
            }
          },
          "409": {
            "description": "The plan has no overage to override",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CustomerError"
                }
              }
            }
          },
          "413": {
            "$ref": "#/components/responses/PayloadTooLarge"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        },
        "summary": "Set or clear a customer's negotiated overage rate",
        "x-codeSamples": [
          {
            "lang": "python",
            "label": "Python SDK",
            "source": "import os\nfrom integrateledger import ledger\n\nclient = ledger(api_key=os.environ[\"LEDGER_API_KEY\"])\nclient.set_customer_overage_override(customer_id=\"user_123\", plan_id=\"pro\", unit_amount_cents=8, billing_units=1)"
          },
          {
            "lang": "bash",
            "label": "cURL",
            "source": "curl --request POST 'https://api.integrateledger.com/v1/customers/overage-override?customerId=user_123' \\\n  -H \"Authorization: Bearer $LEDGER_API_KEY\" \\\n  -H 'Content-Type: application/json' \\\n  --data '{\"planId\":\"pro\",\"unitAmountCents\":8,\"billingUnits\":1}'"
          },
          {
            "lang": "typescript",
            "label": "TypeScript SDK",
            "source": "import ledger from \"integrateledger\";\n\nconst client = new ledger({ apiKey: process.env.LEDGER_API_KEY ?? \"\" });\nawait client.setCustomerOverageOverride({ customerId: \"user_123\", body: { planId: \"pro\", unitAmountCents: 8, billingUnits: 1 } });"
          }
        ],
        "description": "Overrides only change the terms of a plan that already has overage. Send unitAmountCents: null (with the same planId) to clear the override and restore the plan rate.",
        "tags": [
          "Customers"
        ]
      }
    },
    "/v1/customers/balance-adjustments": {
      "post": {
        "operationId": "adjustCustomerBalance",
        "security": [
          {
            "ApiKey": []
          }
        ],
        "parameters": [
          {
            "schema": {
              "type": "string",
              "minLength": 1,
              "maxLength": 255
            },
            "required": true,
            "name": "customerId",
            "in": "query"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/BalanceAdjustmentInput"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Credits added or removed. A retry with the same idempotencyKey returns the first adjustment, with the Idempotent-Replayed: true header.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/BalanceAdjustmentResult"
                }
              }
            }
          },
          "400": {
            "description": "Invalid request, or expiresAt isn't after the customer's current time",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/BalanceAdjustmentError"
                }
              }
            }
          },
          "401": {
            "description": "Missing or invalid Ledger API key, or a key without billing:write",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/BalanceAdjustmentError"
                }
              }
            }
          },
          "404": {
            "description": "Customer not found, or Stripe has no such invoice or Checkout Session in this environment",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/BalanceAdjustmentError"
                }
              }
            }
          },
          "409": {
            "description": "The removal is more than the balance (insufficient_credits), the idempotencyKey was used for a different adjustment, or the Stripe payment is already granted, unpaid, or another customer's",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/BalanceAdjustmentError"
                }
              }
            }
          },
          "413": {
            "$ref": "#/components/responses/PayloadTooLarge"
          },
          "422": {
            "description": "A Stripe payment was linked, but Stripe isn't connected in this environment",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/BalanceAdjustmentError"
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "502": {
            "description": "Stripe didn't answer while Ledger checked the payment",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/BalanceAdjustmentError"
                }
              }
            }
          }
        },
        "summary": "Add or remove a customer's credits by hand",
        "x-codeSamples": [
          {
            "lang": "python",
            "label": "Python SDK",
            "source": "import os\nfrom integrateledger import ledger\n\nclient = ledger(api_key=os.environ[\"LEDGER_API_KEY\"])\nclient.adjust_customer_balance(customer_id=\"user_123\", credits=500, reason=\"Goodwill for the outage\", idempotency_key=\"goodwill-user_123\")"
          },
          {
            "lang": "bash",
            "label": "cURL",
            "source": "curl --request POST 'https://api.integrateledger.com/v1/customers/balance-adjustments?customerId=user_123' \\\n  -H \"Authorization: Bearer $LEDGER_API_KEY\" \\\n  -H 'Content-Type: application/json' \\\n  --data '{\"credits\":500,\"reason\":\"Goodwill for the outage\",\"idempotencyKey\":\"goodwill-user_123\"}'"
          },
          {
            "lang": "typescript",
            "label": "TypeScript SDK",
            "source": "import ledger from \"integrateledger\";\n\nconst client = new ledger({ apiKey: process.env.LEDGER_API_KEY ?? \"\" });\nawait client.adjustCustomerBalance({ customerId: \"user_123\", body: { credits: 500, reason: \"Goodwill for the outage\", idempotencyKey: \"goodwill-user_123\" } });"
          }
        ],
        "description": "Negative credits remove credits, never below a zero balance. Pass stripeInvoiceId (or stripeCheckoutSessionId) to grant a paid Stripe payment that Ledger didn't grant.",
        "tags": [
          "Customers"
        ]
      }
    },
    "/v1/customers/stripe-customer": {
      "post": {
        "operationId": "linkStripeCustomer",
        "description": "Links a customer to its existing Stripe customer, for a customer who was already billed in your Stripe account. Ledger adds its metadata to the Stripe customer and leaves everything else as it is. Later Checkouts and upgrades use this Stripe customer and its saved cards; import its subscription with POST /v1/billing/import. Linking the same Stripe customer again changes nothing.",
        "security": [
          {
            "ApiKey": []
          }
        ],
        "parameters": [
          {
            "schema": {
              "type": "string",
              "minLength": 1,
              "maxLength": 255
            },
            "required": true,
            "name": "customerId",
            "in": "query"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/StripeCustomerLinkInput"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "The customer is linked to the Stripe customer",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/StripeCustomerLink"
                }
              }
            }
          },
          "400": {
            "description": "Invalid request",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CustomerError"
                }
              }
            }
          },
          "401": {
            "description": "Missing or invalid Ledger API key",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CustomerError"
                }
              }
            }
          },
          "404": {
            "description": "No such customer, or no such Stripe customer in this environment's Stripe account",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CustomerError"
                }
              }
            }
          },
          "409": {
            "description": "The customer is linked to another Stripe customer (stripe_customer_already_linked), another customer has this one (stripe_customer_linked_elsewhere), or its metadata names another Ledger workspace or environment (stripe_customer_owned_elsewhere)",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CustomerError"
                }
              }
            }
          },
          "413": {
            "$ref": "#/components/responses/PayloadTooLarge"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "description": "Unexpected error in Ledger. It has been logged with the request ID; retry, or quote request_id when reporting it.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/InternalError"
                }
              }
            }
          },
          "502": {
            "description": "No Stripe connection for this environment or its key is invalid (stripe_not_configured), or Stripe unavailable (stripe_link_failed)",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CustomerError"
                }
              }
            }
          }
        },
        "summary": "Link a customer to its existing Stripe customer",
        "x-codeSamples": [
          {
            "lang": "python",
            "label": "Python SDK",
            "source": "import os\nfrom integrateledger import ledger\n\nclient = ledger(api_key=os.environ[\"LEDGER_API_KEY\"])\nclient.link_stripe_customer(customer_id=\"user_123\", stripe_customer_id=\"cus_123\")"
          },
          {
            "lang": "bash",
            "label": "cURL",
            "source": "curl --request POST 'https://api.integrateledger.com/v1/customers/stripe-customer?customerId=user_123' \\\n  -H \"Authorization: Bearer $LEDGER_API_KEY\" \\\n  -H 'Content-Type: application/json' \\\n  --data '{\"stripeCustomerId\":\"cus_123\"}'"
          },
          {
            "lang": "typescript",
            "label": "TypeScript SDK",
            "source": "import ledger from \"integrateledger\";\n\nconst client = new ledger({ apiKey: process.env.LEDGER_API_KEY ?? \"\" });\nawait client.linkStripeCustomer({ customerId: \"user_123\", body: { stripeCustomerId: \"cus_123\" } });"
          }
        ],
        "tags": [
          "Customers"
        ]
      }
    },
    "/v1/track": {
      "post": {
        "operationId": "trackUsage",
        "security": [
          {
            "ApiKey": []
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/TrackInput"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Usage recorded or previously recorded",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/TrackResponse"
                }
              }
            }
          },
          "400": {
            "description": "Invalid input",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/TrackError"
                }
              }
            }
          },
          "401": {
            "description": "Invalid Ledger API key",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/TrackError"
                }
              }
            }
          },
          "404": {
            "description": "Customer or feature not found",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/TrackError"
                }
              }
            }
          },
          "409": {
            "description": "Insufficient credits, overage cap reached, or request ID conflict",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/TrackError"
                }
              }
            }
          },
          "413": {
            "$ref": "#/components/responses/PayloadTooLarge"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        },
        "summary": "Record feature usage",
        "x-codeSamples": [
          {
            "lang": "python",
            "label": "Python SDK",
            "source": "import os\nfrom integrateledger import ledger\n\nclient = ledger(api_key=os.environ[\"LEDGER_API_KEY\"])\nclient.track_usage(customer_id=\"user_123\", feature_id=\"api_call\", request_id=\"operation_123\")"
          },
          {
            "lang": "bash",
            "label": "cURL",
            "source": "curl --request POST 'https://api.integrateledger.com/v1/track' \\\n  -H \"Authorization: Bearer $LEDGER_API_KEY\" \\\n  -H 'Content-Type: application/json' \\\n  --data '{\"customerId\":\"user_123\",\"featureId\":\"api_call\",\"request_id\":\"operation_123\"}'"
          },
          {
            "lang": "typescript",
            "label": "TypeScript SDK",
            "source": "import ledger from \"integrateledger\";\n\nconst client = new ledger({ apiKey: process.env.LEDGER_API_KEY ?? \"\" });\nawait client.trackUsage({ customerId: \"user_123\", featureId: \"api_call\", requestId: \"operation_123\" });"
          }
        ],
        "tags": [
          "Usage"
        ]
      }
    },
    "/v1/billing/attach": {
      "post": {
        "operationId": "attach",
        "security": [
          {
            "ApiKey": []
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/AttachInput"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Pending Stripe Checkout for a paid item, or an active plan: a free plan, or a paid plan charged to the customer's saved card. A saved card that needs the customer to authenticate (3-D Secure) isn't charged: the response is a Checkout URL, where the customer authenticates while paying.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/AttachResponse"
                }
              }
            }
          },
          "400": {
            "description": "Invalid request",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/AttachError"
                }
              }
            }
          },
          "401": {
            "description": "Invalid Ledger API key",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/AttachError"
                }
              }
            }
          },
          "402": {
            "description": "The saved card was declined (payment_failed). No subscription was created, and the customer can attach again.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/AttachError"
                }
              }
            }
          },
          "404": {
            "description": "Customer, plan, or add-on not found",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/AttachError"
                }
              }
            }
          },
          "409": {
            "description": "Customer already has a subscription, another Checkout is pending, or Checkout expired",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/AttachError"
                }
              }
            }
          },
          "413": {
            "$ref": "#/components/responses/PayloadTooLarge"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "description": "Unexpected error in Ledger. It has been logged with the request ID; retry, or quote request_id when reporting it.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/InternalError"
                }
              }
            }
          },
          "502": {
            "description": "Stripe unavailable or refused the request (stripe_checkout_failed), Stripe connection incomplete or its key invalid or missing a permission (stripe_not_configured), or signed webhook endpoint unhealthy (stripe_webhook_unhealthy); retry after resolving",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/AttachError"
                }
              }
            }
          },
          "503": {
            "description": "Live billing is not enabled",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/AttachError"
                }
              }
            }
          }
        },
        "summary": "Attach a plan or add-on to a customer",
        "x-codeSamples": [
          {
            "lang": "python",
            "label": "Python SDK",
            "source": "import os\nfrom integrateledger import ledger\n\nclient = ledger(api_key=os.environ[\"LEDGER_API_KEY\"])\nclient.attach(customer_id=\"user_123\", plan_id=\"starter\")"
          },
          {
            "lang": "bash",
            "label": "cURL",
            "source": "curl --request POST 'https://api.integrateledger.com/v1/billing/attach' \\\n  -H \"Authorization: Bearer $LEDGER_API_KEY\" \\\n  -H 'Content-Type: application/json' \\\n  --data '{\"customerId\":\"user_123\",\"planId\":\"starter\"}'"
          },
          {
            "lang": "typescript",
            "label": "TypeScript SDK",
            "source": "import ledger from \"integrateledger\";\n\nconst client = new ledger({ apiKey: process.env.LEDGER_API_KEY ?? \"\" });\nawait client.attach({ customerId: \"user_123\", planId: \"starter\" });"
          }
        ],
        "description": "For a credit pack, send addOnId instead of planId.",
        "tags": [
          "Billing"
        ]
      }
    },
    "/v1/billing/upgrade": {
      "post": {
        "operationId": "upgradePaidPlan",
        "security": [
          {
            "ApiKey": []
          }
        ],
        "parameters": [
          {
            "schema": {
              "type": "string",
              "minLength": 1,
              "maxLength": 255
            },
            "required": true,
            "name": "idempotency-key",
            "in": "header"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/UpgradeInput"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Current upgrade outcome",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/UpgradeResponse"
                }
              }
            }
          },
          "400": {
            "description": "Invalid or ineligible upgrade",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/UpgradeError"
                }
              }
            }
          },
          "401": {
            "description": "Invalid Ledger API key",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "error": {
                      "type": "string"
                    }
                  },
                  "required": [
                    "error"
                  ]
                }
              }
            }
          },
          "404": {
            "description": "Unknown customer or target plan",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "error": {
                      "type": "string"
                    }
                  },
                  "required": [
                    "error"
                  ]
                }
              }
            }
          },
          "409": {
            "description": "Idempotency key mismatch, a pending upgrade, or a scheduled downgrade (downgrade_scheduled)",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "error": {
                      "type": "string"
                    }
                  },
                  "required": [
                    "error"
                  ]
                }
              }
            }
          },
          "413": {
            "$ref": "#/components/responses/PayloadTooLarge"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "description": "Unexpected error in Ledger. It has been logged with the request ID; retry, or quote request_id when reporting it.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/InternalError"
                }
              }
            }
          },
          "502": {
            "description": "Stripe unavailable or refused the request (stripe_upgrade_failed; retry with the same key), Stripe connection incomplete or its key invalid or missing a permission (stripe_not_configured), or signed webhook endpoint unhealthy (stripe_webhook_unhealthy)",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "error": {
                      "type": "string"
                    }
                  },
                  "required": [
                    "error"
                  ]
                }
              }
            }
          },
          "503": {
            "description": "Upgrade requires Stripe reconciliation or payment action",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "error": {
                      "type": "string"
                    },
                    "upgradeId": {
                      "type": "integer"
                    }
                  },
                  "required": [
                    "error"
                  ]
                }
              }
            }
          }
        },
        "summary": "Upgrade a paid plan",
        "x-codeSamples": [
          {
            "lang": "python",
            "label": "Python SDK",
            "source": "import os\nfrom integrateledger import ledger\n\nclient = ledger(api_key=os.environ[\"LEDGER_API_KEY\"])\nclient.upgrade_paid_plan(customer_id=\"user_123\", plan_id=\"pro\", idempotency_key=\"upgrade_123\")"
          },
          {
            "lang": "bash",
            "label": "cURL",
            "source": "curl --request POST 'https://api.integrateledger.com/v1/billing/upgrade' \\\n  -H \"Authorization: Bearer $LEDGER_API_KEY\" \\\n  -H 'Idempotency-Key: upgrade_123' \\\n  -H 'Content-Type: application/json' \\\n  --data '{\"customerId\":\"user_123\",\"planId\":\"pro\"}'"
          },
          {
            "lang": "typescript",
            "label": "TypeScript SDK",
            "source": "import ledger from \"integrateledger\";\n\nconst client = new ledger({ apiKey: process.env.LEDGER_API_KEY ?? \"\" });\nawait client.upgradePaidPlan({ idempotencyKey: \"upgrade_123\", body: { customerId: \"user_123\", planId: \"pro\" } });"
          }
        ],
        "tags": [
          "Billing"
        ]
      }
    },
    "/v1/billing/import": {
      "post": {
        "operationId": "importSubscription",
        "description": "Link the Stripe customer first. Add startingBalance to carry over the credits the customer has left this period.",
        "security": [
          {
            "ApiKey": []
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/ImportSubscriptionInput"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "The subscription is the customer's Ledger plan",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ImportSubscriptionResponse"
                }
              }
            }
          },
          "400": {
            "description": "Invalid request, a free plan, a startingBalance above the plan's credits, or a subscription Ledger can't bill: past_due, incomplete, ended or not billable, an unpaid latest invoice, or unsupported_subscription with a reason",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ImportError"
                }
              }
            }
          },
          "401": {
            "description": "Invalid Ledger API key",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ImportError"
                }
              }
            }
          },
          "404": {
            "description": "Customer, plan or Stripe subscription not found",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ImportError"
                }
              }
            }
          },
          "409": {
            "description": "The Stripe customer isn't linked or isn't the subscription's, it has another open subscription, the Ledger customer already has a plan, or the subscription was imported onto another plan",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ImportError"
                }
              }
            }
          },
          "413": {
            "$ref": "#/components/responses/PayloadTooLarge"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "description": "Unexpected error in Ledger. It has been logged with the request ID; retry, or quote request_id when reporting it.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/InternalError"
                }
              }
            }
          },
          "502": {
            "description": "Stripe unavailable or refused the request (stripe_import_failed), the Stripe connection is incomplete or its key is invalid (stripe_not_configured), or the signed webhook endpoint is unhealthy (stripe_webhook_unhealthy)",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ImportError"
                }
              }
            }
          }
        },
        "summary": "Import an existing Stripe subscription",
        "x-codeSamples": [
          {
            "lang": "python",
            "label": "Python SDK",
            "source": "import os\nfrom integrateledger import ledger\n\nclient = ledger(api_key=os.environ[\"LEDGER_API_KEY\"])\nclient.import_subscription(customer_id=\"user_123\", stripe_subscription_id=\"sub_123\", plan_id=\"pro\")"
          },
          {
            "lang": "bash",
            "label": "cURL",
            "source": "curl --request POST 'https://api.integrateledger.com/v1/billing/import' \\\n  -H \"Authorization: Bearer $LEDGER_API_KEY\" \\\n  -H 'Content-Type: application/json' \\\n  --data '{\"customerId\":\"user_123\",\"stripeSubscriptionId\":\"sub_123\",\"planId\":\"pro\"}'"
          },
          {
            "lang": "typescript",
            "label": "TypeScript SDK",
            "source": "import ledger from \"integrateledger\";\n\nconst client = new ledger({ apiKey: process.env.LEDGER_API_KEY ?? \"\" });\nawait client.importSubscription({ customerId: \"user_123\", stripeSubscriptionId: \"sub_123\", planId: \"pro\" });"
          }
        ],
        "tags": [
          "Billing"
        ]
      }
    },
    "/v1/billing/downgrade": {
      "post": {
        "operationId": "downgradePaidPlan",
        "security": [
          {
            "ApiKey": []
          }
        ],
        "parameters": [
          {
            "schema": {
              "type": "string",
              "minLength": 1,
              "maxLength": 255
            },
            "required": true,
            "name": "idempotency-key",
            "in": "header"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/DowngradeInput"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "The downgrade: `scheduled` until the period ends, or `applied`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Downgrade"
                }
              }
            }
          },
          "400": {
            "description": "Invalid or ineligible downgrade (ineligible_downgrade, refund_not_available, overage_option_not_available, invalid_idempotency_key)",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "error": {
                      "type": "string"
                    },
                    "downgradeId": {
                      "type": "integer"
                    }
                  },
                  "required": [
                    "error"
                  ]
                }
              }
            }
          },
          "401": {
            "description": "Invalid Ledger API key",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "error": {
                      "type": "string"
                    },
                    "downgradeId": {
                      "type": "integer"
                    }
                  },
                  "required": [
                    "error"
                  ]
                }
              }
            }
          },
          "404": {
            "description": "Unknown customer or target plan",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "error": {
                      "type": "string"
                    },
                    "downgradeId": {
                      "type": "integer"
                    }
                  },
                  "required": [
                    "error"
                  ]
                }
              }
            }
          },
          "409": {
            "description": "Idempotency key reused with a different request, or another change pending (upgrade_pending, downgrade_scheduled, cancellation_scheduled, no_payment_to_refund)",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "error": {
                      "type": "string"
                    },
                    "downgradeId": {
                      "type": "integer"
                    }
                  },
                  "required": [
                    "error"
                  ]
                }
              }
            }
          },
          "413": {
            "$ref": "#/components/responses/PayloadTooLarge"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "description": "Unexpected error in Ledger. It has been logged with the request ID; retry, or quote request_id when reporting it.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/InternalError"
                }
              }
            }
          },
          "502": {
            "description": "Stripe unavailable or refused the request (stripe_downgrade_failed; retry with the same key), or Stripe not set up",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "error": {
                      "type": "string"
                    },
                    "downgradeId": {
                      "type": "integer"
                    }
                  },
                  "required": [
                    "error"
                  ]
                }
              }
            }
          },
          "503": {
            "description": "Live charges disabled, or the old plan's overage invoice still being created (overage_billing_pending; retry with the same key)",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "error": {
                      "type": "string"
                    },
                    "downgradeId": {
                      "type": "integer"
                    }
                  },
                  "required": [
                    "error"
                  ]
                }
              }
            }
          }
        },
        "summary": "Downgrade a paid plan",
        "x-codeSamples": [
          {
            "lang": "python",
            "label": "Python SDK",
            "source": "import os\nfrom integrateledger import ledger\n\nclient = ledger(api_key=os.environ[\"LEDGER_API_KEY\"])\nclient.downgrade_paid_plan(customer_id=\"user_123\", plan_id=\"starter\", timing=\"period_end\", idempotency_key=\"downgrade_123\")"
          },
          {
            "lang": "bash",
            "label": "cURL",
            "source": "curl --request POST 'https://api.integrateledger.com/v1/billing/downgrade' \\\n  -H \"Authorization: Bearer $LEDGER_API_KEY\" \\\n  -H 'Idempotency-Key: downgrade_123' \\\n  -H 'Content-Type: application/json' \\\n  --data '{\"customerId\":\"user_123\",\"planId\":\"starter\",\"timing\":\"period_end\"}'"
          },
          {
            "lang": "typescript",
            "label": "TypeScript SDK",
            "source": "import ledger from \"integrateledger\";\n\nconst client = new ledger({ apiKey: process.env.LEDGER_API_KEY ?? \"\" });\nawait client.downgradePaidPlan({ idempotencyKey: \"downgrade_123\", body: { customerId: \"user_123\", planId: \"starter\", timing: \"period_end\" } });"
          }
        ],
        "tags": [
          "Billing"
        ]
      },
      "get": {
        "operationId": "getScheduledDowngrade",
        "security": [
          {
            "ApiKey": []
          }
        ],
        "parameters": [
          {
            "schema": {
              "type": "string",
              "minLength": 1,
              "maxLength": 255
            },
            "required": true,
            "name": "customerId",
            "in": "query"
          }
        ],
        "responses": {
          "200": {
            "description": "The customer's downgrade waiting for its period end",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ScheduledDowngrade"
                }
              }
            }
          },
          "400": {
            "description": "Invalid query",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "error": {
                      "type": "string"
                    },
                    "downgradeId": {
                      "type": "integer"
                    }
                  },
                  "required": [
                    "error"
                  ]
                }
              }
            }
          },
          "401": {
            "description": "Invalid Ledger API key",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "error": {
                      "type": "string"
                    },
                    "downgradeId": {
                      "type": "integer"
                    }
                  },
                  "required": [
                    "error"
                  ]
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        },
        "summary": "Get a customer's scheduled downgrade",
        "x-codeSamples": [
          {
            "lang": "python",
            "label": "Python SDK",
            "source": "import os\nfrom integrateledger import ledger\n\nclient = ledger(api_key=os.environ[\"LEDGER_API_KEY\"])\nclient.get_scheduled_downgrade(customer_id=\"user_123\")"
          },
          {
            "lang": "bash",
            "label": "cURL",
            "source": "curl --request GET 'https://api.integrateledger.com/v1/billing/downgrade?customerId=user_123' \\\n  -H \"Authorization: Bearer $LEDGER_API_KEY\""
          },
          {
            "lang": "typescript",
            "label": "TypeScript SDK",
            "source": "import ledger from \"integrateledger\";\n\nconst client = new ledger({ apiKey: process.env.LEDGER_API_KEY ?? \"\" });\nawait client.getScheduledDowngrade({ customerId: \"user_123\" });"
          }
        ],
        "tags": [
          "Billing"
        ]
      }
    },
    "/v1/billing/downgrade/{downgradeId}/cancel": {
      "post": {
        "operationId": "cancelScheduledDowngrade",
        "security": [
          {
            "ApiKey": []
          }
        ],
        "parameters": [
          {
            "schema": {
              "type": "integer",
              "exclusiveMinimum": 0,
              "maximum": 2147483647
            },
            "required": true,
            "name": "downgradeId",
            "in": "path"
          },
          {
            "schema": {
              "type": "string",
              "minLength": 1,
              "maxLength": 255
            },
            "required": true,
            "name": "customerId",
            "in": "query"
          }
        ],
        "responses": {
          "200": {
            "description": "The downgrade, now `closed`; the subscription stays on its current plan",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Downgrade"
                }
              }
            }
          },
          "400": {
            "description": "Invalid request",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "error": {
                      "type": "string"
                    },
                    "downgradeId": {
                      "type": "integer"
                    }
                  },
                  "required": [
                    "error"
                  ]
                }
              }
            }
          },
          "401": {
            "description": "Invalid Ledger API key",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "error": {
                      "type": "string"
                    },
                    "downgradeId": {
                      "type": "integer"
                    }
                  },
                  "required": [
                    "error"
                  ]
                }
              }
            }
          },
          "404": {
            "description": "No such downgrade for this customer",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "error": {
                      "type": "string"
                    },
                    "downgradeId": {
                      "type": "integer"
                    }
                  },
                  "required": [
                    "error"
                  ]
                }
              }
            }
          },
          "409": {
            "description": "Already applied (downgrade_applied); upgrade instead",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "error": {
                      "type": "string"
                    },
                    "downgradeId": {
                      "type": "integer"
                    }
                  },
                  "required": [
                    "error"
                  ]
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "description": "Unexpected error in Ledger. It has been logged with the request ID; retry, or quote request_id when reporting it.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/InternalError"
                }
              }
            }
          },
          "502": {
            "description": "Stripe unavailable or not set up",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "error": {
                      "type": "string"
                    },
                    "downgradeId": {
                      "type": "integer"
                    }
                  },
                  "required": [
                    "error"
                  ]
                }
              }
            }
          }
        },
        "summary": "Undo a scheduled downgrade",
        "x-codeSamples": [
          {
            "lang": "python",
            "label": "Python SDK",
            "source": "import os\nfrom integrateledger import ledger\n\nclient = ledger(api_key=os.environ[\"LEDGER_API_KEY\"])\nclient.cancel_scheduled_downgrade(customer_id=\"user_123\", downgrade_id=7)"
          },
          {
            "lang": "bash",
            "label": "cURL",
            "source": "curl --request POST 'https://api.integrateledger.com/v1/billing/downgrade/7/cancel?customerId=user_123' \\\n  -H \"Authorization: Bearer $LEDGER_API_KEY\""
          },
          {
            "lang": "typescript",
            "label": "TypeScript SDK",
            "source": "import ledger from \"integrateledger\";\n\nconst client = new ledger({ apiKey: process.env.LEDGER_API_KEY ?? \"\" });\nawait client.cancelScheduledDowngrade({ downgradeId: 7, customerId: \"user_123\" });"
          }
        ],
        "tags": [
          "Billing"
        ]
      }
    },
    "/v1/migrate/stripe/preview": {
      "get": {
        "operationId": "previewStripeMigration",
        "description": "Or run the whole migration with npx integrateledger-cli migrate stripe.",
        "security": [
          {
            "ApiKey": []
          }
        ],
        "responses": {
          "200": {
            "description": "What a migration would import and refuse",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/MigrationPreview"
                }
              }
            }
          },
          "401": {
            "description": "Missing or invalid Ledger API key",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/MigrationError"
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "description": "Unexpected error in Ledger. It has been logged with the request ID; retry, or quote request_id when reporting it.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/InternalError"
                }
              }
            }
          },
          "502": {
            "description": "No Stripe connection for this environment or its key is invalid (stripe_not_configured), or Stripe unavailable (stripe_preview_failed)",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/MigrationError"
                }
              }
            }
          }
        },
        "summary": "Preview a migration from Stripe Billing",
        "x-codeSamples": [
          {
            "lang": "python",
            "label": "Python SDK",
            "source": "import os\nfrom integrateledger import ledger\n\nclient = ledger(api_key=os.environ[\"LEDGER_API_KEY\"])\nclient.preview_stripe_migration()"
          },
          {
            "lang": "bash",
            "label": "cURL",
            "source": "curl --request GET 'https://api.integrateledger.com/v1/migrate/stripe/preview' \\\n  -H \"Authorization: Bearer $LEDGER_API_KEY\""
          },
          {
            "lang": "typescript",
            "label": "TypeScript SDK",
            "source": "import ledger from \"integrateledger\";\n\nconst client = new ledger({ apiKey: process.env.LEDGER_API_KEY ?? \"\" });\nawait client.previewStripeMigration();"
          }
        ],
        "tags": [
          "Billing"
        ]
      }
    },
    "/v1/webhook-endpoints": {
      "get": {
        "operationId": "listWebhookEndpoints",
        "security": [
          {
            "ApiKey": []
          }
        ],
        "responses": {
          "200": {
            "description": "The environment's endpoints",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/WebhookEndpointList"
                }
              }
            }
          },
          "401": {
            "description": "Invalid Ledger API key",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/WebhookError"
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        },
        "summary": "List webhook endpoints",
        "x-codeSamples": [
          {
            "lang": "python",
            "label": "Python SDK",
            "source": "import os\nfrom integrateledger import ledger\n\nclient = ledger(api_key=os.environ[\"LEDGER_API_KEY\"])\nclient.list_webhook_endpoints()"
          },
          {
            "lang": "bash",
            "label": "cURL",
            "source": "curl --request GET 'https://api.integrateledger.com/v1/webhook-endpoints' \\\n  -H \"Authorization: Bearer $LEDGER_API_KEY\""
          },
          {
            "lang": "typescript",
            "label": "TypeScript SDK",
            "source": "import ledger from \"integrateledger\";\n\nconst client = new ledger({ apiKey: process.env.LEDGER_API_KEY ?? \"\" });\nawait client.listWebhookEndpoints();"
          }
        ],
        "tags": [
          "Webhooks"
        ]
      },
      "post": {
        "operationId": "createWebhookEndpoint",
        "security": [
          {
            "ApiKey": []
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/CreateWebhookEndpointInput"
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Endpoint created, with its signing secret",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CreatedWebhookEndpoint"
                }
              }
            }
          },
          "400": {
            "description": "Invalid request",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/WebhookError"
                }
              }
            }
          },
          "401": {
            "description": "Invalid Ledger API key",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/WebhookError"
                }
              }
            }
          },
          "409": {
            "description": "The environment already has 16 endpoints",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/WebhookError"
                }
              }
            }
          },
          "413": {
            "$ref": "#/components/responses/PayloadTooLarge"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        },
        "summary": "Create a webhook endpoint",
        "x-codeSamples": [
          {
            "lang": "python",
            "label": "Python SDK",
            "source": "import os\nfrom integrateledger import ledger\n\nclient = ledger(api_key=os.environ[\"LEDGER_API_KEY\"])\nclient.create_webhook_endpoint(url=\"https://example.com/webhooks/ledger\", enabled_events=[\"subscription.created\", \"balance.low\"])"
          },
          {
            "lang": "bash",
            "label": "cURL",
            "source": "curl --request POST 'https://api.integrateledger.com/v1/webhook-endpoints' \\\n  -H \"Authorization: Bearer $LEDGER_API_KEY\" \\\n  -H 'Content-Type: application/json' \\\n  --data '{\"url\":\"https://example.com/webhooks/ledger\",\"enabledEvents\":[\"subscription.created\",\"balance.low\"]}'"
          },
          {
            "lang": "typescript",
            "label": "TypeScript SDK",
            "source": "import ledger from \"integrateledger\";\n\nconst client = new ledger({ apiKey: process.env.LEDGER_API_KEY ?? \"\" });\nawait client.createWebhookEndpoint({ url: \"https://example.com/webhooks/ledger\", enabledEvents: [\"subscription.created\", \"balance.low\"] });"
          }
        ],
        "description": "The response's secret is returned only here. Store it and verify each request's Ledger-Signature header with it; see the Webhooks guide.",
        "tags": [
          "Webhooks"
        ]
      }
    },
    "/v1/webhook-endpoints/{id}": {
      "get": {
        "operationId": "getWebhookEndpoint",
        "security": [
          {
            "ApiKey": []
          }
        ],
        "parameters": [
          {
            "schema": {
              "type": "string",
              "minLength": 1,
              "maxLength": 64,
              "pattern": "^we_[A-Za-z0-9]+$"
            },
            "required": true,
            "name": "id",
            "in": "path"
          }
        ],
        "responses": {
          "200": {
            "description": "The endpoint",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/WebhookEndpoint"
                }
              }
            }
          },
          "400": {
            "description": "Invalid request",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/WebhookError"
                }
              }
            }
          },
          "401": {
            "description": "Invalid Ledger API key",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/WebhookError"
                }
              }
            }
          },
          "404": {
            "description": "Endpoint not found",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/WebhookError"
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        },
        "summary": "Get a webhook endpoint",
        "x-codeSamples": [
          {
            "lang": "python",
            "label": "Python SDK",
            "source": "import os\nfrom integrateledger import ledger\n\nclient = ledger(api_key=os.environ[\"LEDGER_API_KEY\"])\nclient.get_webhook_endpoint(\"we_123\")"
          },
          {
            "lang": "bash",
            "label": "cURL",
            "source": "curl --request GET 'https://api.integrateledger.com/v1/webhook-endpoints/we_123' \\\n  -H \"Authorization: Bearer $LEDGER_API_KEY\""
          },
          {
            "lang": "typescript",
            "label": "TypeScript SDK",
            "source": "import ledger from \"integrateledger\";\n\nconst client = new ledger({ apiKey: process.env.LEDGER_API_KEY ?? \"\" });\nawait client.getWebhookEndpoint({ id: \"we_123\" });"
          }
        ],
        "tags": [
          "Webhooks"
        ]
      },
      "post": {
        "operationId": "updateWebhookEndpoint",
        "security": [
          {
            "ApiKey": []
          }
        ],
        "parameters": [
          {
            "schema": {
              "type": "string",
              "minLength": 1,
              "maxLength": 64,
              "pattern": "^we_[A-Za-z0-9]+$"
            },
            "required": true,
            "name": "id",
            "in": "path"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/UpdateWebhookEndpointInput"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Endpoint updated",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/WebhookEndpoint"
                }
              }
            }
          },
          "400": {
            "description": "Invalid request",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/WebhookError"
                }
              }
            }
          },
          "401": {
            "description": "Invalid Ledger API key",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/WebhookError"
                }
              }
            }
          },
          "404": {
            "description": "Endpoint not found",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/WebhookError"
                }
              }
            }
          },
          "413": {
            "$ref": "#/components/responses/PayloadTooLarge"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        },
        "summary": "Update a webhook endpoint",
        "x-codeSamples": [
          {
            "lang": "python",
            "label": "Python SDK",
            "source": "import os\nfrom integrateledger import ledger\n\nclient = ledger(api_key=os.environ[\"LEDGER_API_KEY\"])\nclient.update_webhook_endpoint(\"we_123\", enabled_events=[\"*\"], disabled=False)"
          },
          {
            "lang": "bash",
            "label": "cURL",
            "source": "curl --request POST 'https://api.integrateledger.com/v1/webhook-endpoints/we_123' \\\n  -H \"Authorization: Bearer $LEDGER_API_KEY\" \\\n  -H 'Content-Type: application/json' \\\n  --data '{\"enabledEvents\":[\"*\"],\"disabled\":false}'"
          },
          {
            "lang": "typescript",
            "label": "TypeScript SDK",
            "source": "import ledger from \"integrateledger\";\n\nconst client = new ledger({ apiKey: process.env.LEDGER_API_KEY ?? \"\" });\nawait client.updateWebhookEndpoint({ id: \"we_123\", body: { enabledEvents: [\"*\"], disabled: false } });"
          }
        ],
        "description": "Send disabled: false to turn an endpoint back on, including after Ledger turned it off for failing deliveries.",
        "tags": [
          "Webhooks"
        ]
      },
      "delete": {
        "operationId": "deleteWebhookEndpoint",
        "security": [
          {
            "ApiKey": []
          }
        ],
        "parameters": [
          {
            "schema": {
              "type": "string",
              "minLength": 1,
              "maxLength": 64,
              "pattern": "^we_[A-Za-z0-9]+$"
            },
            "required": true,
            "name": "id",
            "in": "path"
          }
        ],
        "responses": {
          "200": {
            "description": "Endpoint deleted",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/DeletedWebhookEndpoint"
                }
              }
            }
          },
          "400": {
            "description": "Invalid request",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/WebhookError"
                }
              }
            }
          },
          "401": {
            "description": "Invalid Ledger API key",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/WebhookError"
                }
              }
            }
          },
          "404": {
            "description": "Endpoint not found",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/WebhookError"
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        },
        "summary": "Delete a webhook endpoint",
        "x-codeSamples": [
          {
            "lang": "python",
            "label": "Python SDK",
            "source": "import os\nfrom integrateledger import ledger\n\nclient = ledger(api_key=os.environ[\"LEDGER_API_KEY\"])\nclient.delete_webhook_endpoint(\"we_123\")"
          },
          {
            "lang": "bash",
            "label": "cURL",
            "source": "curl --request DELETE 'https://api.integrateledger.com/v1/webhook-endpoints/we_123' \\\n  -H \"Authorization: Bearer $LEDGER_API_KEY\""
          },
          {
            "lang": "typescript",
            "label": "TypeScript SDK",
            "source": "import ledger from \"integrateledger\";\n\nconst client = new ledger({ apiKey: process.env.LEDGER_API_KEY ?? \"\" });\nawait client.deleteWebhookEndpoint({ id: \"we_123\" });"
          }
        ],
        "tags": [
          "Webhooks"
        ]
      }
    }
  },
  "servers": [
    {
      "url": "https://api.integrateledger.com",
      "description": "Hosted Ledger API"
    }
  ]
}
