{"components":{"responses":{"Error":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"An error. See Errors."}},"schemas":{"RunRateMovement":{"additionalProperties":false,"description":"A run rate movement is one change to what a subscription item is worth: a new\ncustomer, an expansion, a contraction, a churn or a reactivation. The\nmetrics report's revenue values add up from these, and a customer's rows\nthat share a `counts_at` make up one movement.\n","example":{"arr":"15000.00","counts_at":"2026-06-12T15:47:09Z","currency_code":"USD","customer_id":"0199a7c2-4e1b-7f38-b9d2-5a6c8e0f1d73","delta":"250.00","movement_type":"expansion","mrr":"1250.00","object":"run_rate_movement","occurred_at":"2026-06-12T15:47:09Z","plan_id":"0198f1a4-1d2e-7a40-9b31-5c6d7e8f9a0b","previous_arr":"12000.00","previous_mrr":"1000.00","previous_plan_id":"0198f1a4-1d2e-7a40-9b31-5c6d7e8f9a0b","previous_price_id":"0198f1a4-2e5f-7b80-a1c2-3d4e5f6a7b8c","previous_quantity":"20","previous_status":"active","price_id":"0198f1a4-2e5f-7b80-a1c2-3d4e5f6a7b8c","quantity":"25","revenue_type":"recurring","status":"active","subscription_id":"0199a7c2-5b3e-7a14-8c6d-2f9e4b7a1c05","subscription_item_id":"0199a7c2-5b41-7c62-9d3e-6a1f8b2c4d97"},"properties":{"arr":{"anyOf":[{"type":"null"},{"format":"decimal","type":"string"}],"description":"What the item counts for a year after the change: `mrr` times twelve.","example":"15000.00"},"counts_at":{"description":"When it counts on a chart. Earlier than `occurred_at` when the project's\ncollapse window folded this change into one made shortly before it. A\ncustomer's rows sharing a `counts_at` are one movement.\n","example":"2026-06-12T15:47:09Z","format":"date-time","type":"string"},"currency_code":{"description":"The currency the customer is billed in, by ISO 4217 code.","example":"USD","type":"string"},"customer":{"anyOf":[{"type":"null"},{"allOf":[{"$ref":"#/components/schemas/Customer"}]}],"description":"Whose revenue moved, as a record — expand it to get them on each row. Expandable.","example":{"arr":"15000.00","country_code":"US","created_at":"2025-11-03T16:20:04Z","email":"billing@northwind.example.com","first_paying_at":"2025-12-01T10:04:00Z","first_seen_at":"2025-11-03T16:20:00Z","first_subscribed_at":"2025-11-17T10:04:00Z","id":"0199a7c2-4e1b-7f38-b9d2-5a6c8e0f1d73","identities":["stripe:cus_QX7bN2kLp4Rt9v","backend:acct_4821"],"mrr":"1250.00","name":"Northwind Labs","object":"customer","properties":{"seats":25},"properties_by_source":{"backend":{"seats":25},"stripe":{"email":"billing@northwind.example.com","name":"Northwind Labs"}},"signed_up_at":"2025-11-03T16:20:00Z","status":"paying"}},"customer_id":{"description":"Whose revenue moved.","example":"0199a7c2-4e1b-7f38-b9d2-5a6c8e0f1d73","format":"uuid","type":"string"},"delta":{"description":"What this change moved the item's revenue run rate by.","example":"250.00","format":"decimal","type":"string"},"movement_type":{"description":"What the movement did to the customer's run rate, shared by every row of it.","enum":["new","reactivation","expansion","contraction","churn","no_change"],"example":"expansion","type":"string"},"mrr":{"description":"What the item counts for a month after the change.","example":"1250.00","format":"decimal","type":"string"},"object":{"description":"The object's type, always `run_rate_movement`.","enum":["run_rate_movement"],"example":"run_rate_movement","type":"string"},"occurred_at":{"description":"When the change happened.","example":"2026-06-12T15:47:09Z","format":"date-time","type":"string"},"plan":{"anyOf":[{"type":"null"},{"allOf":[{"$ref":"#/components/schemas/Plan"}]}],"description":"The plan the item is on after the change, if any — expand it to get it. Expandable.","example":{"archived":false,"created_at":"2025-09-08T14:12:30Z","id":"0198f1a4-1d2e-7a40-9b31-5c6d7e8f9a0b","identities":["stripe:prod_QhR2sT4uV6wX8y"],"name":"Growth","object":"plan","price_count":2,"properties":{"tier":"mid"},"properties_by_source":{"backend":{"tier":"mid"}}}},"plan_id":{"anyOf":[{"type":"null"},{"format":"uuid","type":"string"}],"description":"The plan its price is on, if any. Null when the item went away.","example":"0198f1a4-1d2e-7a40-9b31-5c6d7e8f9a0b"},"previous_arr":{"anyOf":[{"type":"null"},{"format":"decimal","type":"string"}],"description":"And what it counted for a year before: `previous_mrr` times twelve.","example":"12000.00"},"previous_mrr":{"description":"And what it counted for before.","example":"1000.00","format":"decimal","type":"string"},"previous_plan":{"anyOf":[{"type":"null"},{"allOf":[{"$ref":"#/components/schemas/Plan"}]}],"description":"The plan it was on before, if any — expand it to get it. Expandable.","example":{"archived":false,"created_at":"2025-09-08T14:12:30Z","id":"0198f1a4-1d2e-7a40-9b31-5c6d7e8f9a0b","identities":["stripe:prod_QhR2sT4uV6wX8y"],"name":"Growth","object":"plan","price_count":2,"properties":{"tier":"mid"},"properties_by_source":{"backend":{"tier":"mid"}}}},"previous_plan_id":{"anyOf":[{"type":"null"},{"format":"uuid","type":"string"}],"description":"The plan its previous price is on, if any. Null when the item is new.","example":"0198f1a4-1d2e-7a40-9b31-5c6d7e8f9a0b"},"previous_price":{"anyOf":[{"type":"null"},{"allOf":[{"$ref":"#/components/schemas/Price"}]}],"description":"The price it was on before, if any — expand it to get it. Expandable.","example":{"archived":false,"created_at":"2025-09-08T14:12:30Z","currency_code":"USD","id":"0198f1a4-2e5f-7b80-a1c2-3d4e5f6a7b8c","identities":["stripe:price_1PqR8sT2uV4wX6yZ"],"interval":"month","interval_count":1,"name":"Growth monthly","object":"price","owner_source_id":"0198f1a3-0b7c-7e25-9f41-6a2d8c3e5b19","plan_id":"0198f1a4-1d2e-7a40-9b31-5c6d7e8f9a0b","pricing_model":"fixed","properties":{"tier":"mid"},"properties_by_source":{"backend":{"tier":"mid"}},"tax_behavior":"exclusive","unit_amount":"50.00"}},"previous_price_id":{"anyOf":[{"type":"null"},{"format":"uuid","type":"string"}],"description":"The price it was on, if any. Null when the item is new.","example":"0198f1a4-2e5f-7b80-a1c2-3d4e5f6a7b8c"},"previous_quantity":{"description":"And before.","example":"20","format":"decimal","type":"string"},"previous_status":{"anyOf":[{"type":"null"},{"type":"string"}],"description":"Its status before the change. Null when the item is new.","example":"active"},"price":{"anyOf":[{"type":"null"},{"allOf":[{"$ref":"#/components/schemas/Price"}]}],"description":"The price the item is on after the change, if any — expand it to get it. Expandable.","example":{"archived":false,"created_at":"2025-09-08T14:12:30Z","currency_code":"USD","id":"0198f1a4-2e5f-7b80-a1c2-3d4e5f6a7b8c","identities":["stripe:price_1PqR8sT2uV4wX6yZ"],"interval":"month","interval_count":1,"name":"Growth monthly","object":"price","owner_source_id":"0198f1a3-0b7c-7e25-9f41-6a2d8c3e5b19","plan_id":"0198f1a4-1d2e-7a40-9b31-5c6d7e8f9a0b","pricing_model":"fixed","properties":{"tier":"mid"},"properties_by_source":{"backend":{"tier":"mid"}},"tax_behavior":"exclusive","unit_amount":"50.00"}},"price_id":{"anyOf":[{"type":"null"},{"format":"uuid","type":"string"}],"description":"The price the item is on, if any. Null when the item went away.","example":"0198f1a4-2e5f-7b80-a1c2-3d4e5f6a7b8c"},"quantity":{"description":"How many units, after.","example":"25","format":"decimal","type":"string"},"revenue_type":{"description":"What kind of revenue moved: `recurring`.","example":"recurring","type":"string"},"status":{"anyOf":[{"type":"null"},{"type":"string"}],"description":"The item's status after the change — its subscription's while the item is\nthere. Null when the item went away.\n","example":"active"},"subscription_id":{"description":"The subscription the item belongs to.","example":"0199a7c2-5b3e-7a14-8c6d-2f9e4b7a1c05","format":"uuid","type":"string"},"subscription_item_id":{"description":"The item that changed.","example":"0199a7c2-5b41-7c62-9d3e-6a1f8b2c4d97","format":"uuid","type":"string"}},"required":["counts_at","currency_code","customer_id","delta","movement_type","mrr","object","occurred_at","previous_mrr","previous_quantity","quantity","revenue_type","subscription_id","subscription_item_id"],"type":"object"},"Price":{"additionalProperties":false,"description":"A price is what a subscription item is on, in the source it lives in: a\nStripe price, or a plan in a billing system that has no prices. Every\nprice is on one plan, which groups the prices of one thing you sell —\nmonthly and annual, one per currency, or the same plan in two billing\nsystems. A price is created before any subscription item names it.\n","example":{"archived":false,"created_at":"2025-09-08T14:12:30Z","currency_code":"USD","id":"0198f1a4-2e5f-7b80-a1c2-3d4e5f6a7b8c","identities":["stripe:price_1PqR8sT2uV4wX6yZ"],"interval":"month","interval_count":1,"name":"Growth monthly","object":"price","owner_source_id":"0198f1a3-0b7c-7e25-9f41-6a2d8c3e5b19","plan_id":"0198f1a4-1d2e-7a40-9b31-5c6d7e8f9a0b","pricing_model":"fixed","properties":{"tier":"mid"},"properties_by_source":{"backend":{"tier":"mid"}},"tax_behavior":"exclusive","unit_amount":"50.00"},"properties":{"archived":{"description":"Whether the price has stopped being offered. Existing subscriptions keep\nit — what somebody paid for last year does not stop being true when you\nwithdraw the price.\n","example":false,"type":"boolean"},"created_at":{"description":"When the price was first seen.","example":"2025-09-08T14:12:30Z","format":"date-time","type":"string"},"currency_code":{"anyOf":[{"type":"null"},{"maxLength":3,"minLength":3,"type":"string"}],"description":"The currency it is listed in, by its ISO 4217 code.","example":"USD"},"id":{"description":"The price's Summerbase id. Anywhere a price is addressed, one of its `identities` works too.","example":"0198f1a4-2e5f-7b80-a1c2-3d4e5f6a7b8c","format":"uuid","type":"string"},"identities":{"anyOf":[{"type":"null"},{"items":{"type":"string"},"type":"array"}],"description":"What each source calls this price, as `source:id`. Any entry addresses it — `stripe:cus_123`.","example":["stripe:price_1PqR8sT2uV4wX6yZ"]},"interval":{"anyOf":[{"type":"null"},{"enum":["day","week","month","year"],"type":"string"}],"description":"The unit it bills in. None for a price charged once.","example":"month"},"interval_count":{"anyOf":[{"type":"null"},{"minimum":1,"type":"integer"}],"description":"How many intervals between charges.","example":1},"name":{"anyOf":[{"type":"null"},{"maxLength":255,"minLength":1,"type":"string"}],"description":"What the price is called in the source it lives in, when it has a name\nof its own there — a Stripe price's nickname. Most have none, and are\ntold apart by their terms.\n","example":"Growth monthly"},"object":{"description":"The object's type, always `price`.","enum":["price"],"example":"price","type":"string"},"owner_source":{"anyOf":[{"type":"null"},{"allOf":[{"$ref":"#/components/schemas/Integration"}]}],"description":"The source the price came from. Expandable.","example":{"connected_at":"2025-09-08T14:10:05Z","created_at":"2025-09-08T14:09:47Z","current_import_id":"019a0c0f-6b1d-7e3a-8c2f-4d5e6f7a8b9c","id":"0198f1a3-0b7c-7e25-9f41-6a2d8c3e5b19","imported_at":"2025-09-08T14:26:51Z","is_source":true,"name":"Stripe","object":"integration","priority":2,"project_id":"0198f1a2-6c52-7b18-8e3f-2a9c4d6b1e07","provider_account_id":"acct_1QfR3sT8uVw2XyZa","provider_code":"stripe","sandbox":false,"slug":"stripe","webhook_installed_at":"2025-09-08T14:10:06Z","webhook_url":"https://api.summerbase.com/webhooks/integrations/0198f1a3-0b7c-7e25-9f41-6a2d8c3e5b19?c=us1"}},"owner_source_id":{"description":"The source the price came from.","example":"0198f1a3-0b7c-7e25-9f41-6a2d8c3e5b19","format":"uuid","type":"string"},"plan":{"anyOf":[{"type":"null"},{"allOf":[{"$ref":"#/components/schemas/Plan"}]}],"description":"The plan the price is on. Expandable.","example":{"archived":false,"created_at":"2025-09-08T14:12:30Z","id":"0198f1a4-1d2e-7a40-9b31-5c6d7e8f9a0b","identities":["stripe:prod_QhR2sT4uV6wX8y"],"name":"Growth","object":"plan","price_count":2,"properties":{"tier":"mid"},"properties_by_source":{"backend":{"tier":"mid"}}}},"plan_id":{"description":"The plan the price is on.","example":"0198f1a4-1d2e-7a40-9b31-5c6d7e8f9a0b","format":"uuid","type":"string"},"pricing_model":{"anyOf":[{"type":"null"},{"enum":["fixed","tiered","metered"],"type":"string"}],"description":"How an item's unit amount is arrived at on this price.","example":"fixed"},"properties":{"anyOf":[{"type":"null"},{"additionalProperties":{"anyOf":[{"type":"string"},{"type":"number"},{"type":"boolean"}]},"type":"object"}],"description":"**Your** source's properties on this price, as they stand now. What you write here is what you read back — another source's values never appear in it and can never be overwritten through it. `properties_by_source` has everyone's. When you have written a key through several of this price's ids, its value is the most recent change any of them made.","example":{"tier":"mid"}},"properties_by_source":{"anyOf":[{"type":"null"},{"additionalProperties":{"additionalProperties":{"anyOf":[{"type":"string"},{"type":"number"},{"type":"boolean"}]},"description":"Property keys and their current values.","type":"object"},"type":"object"}],"description":"Every source's properties on this price, keyed by source slug — including your own. Read-only: a write only ever reaches the source it is attributed to.","example":{"backend":{"tier":"mid"}}},"tax_behavior":{"description":"Whether the amount has tax in it: `inclusive`, `exclusive`, or\n`unspecified` when the price leaves it to the source's tax settings. A\nsubscription item is worth its amount without any tax in it, at the\nrate its customer's invoices are taxed at, whatever this says.\n","enum":["inclusive","exclusive","unspecified"],"example":"exclusive","type":"string"},"unit_amount":{"anyOf":[{"type":"null"},{"format":"decimal","type":"string"}],"description":"What one unit is listed at per interval: for a `metered` price, one unit\nof usage. None for a `tiered` one, whose units cost what their tier says.\nBelow zero for a credit, which Stripe, for one, lists as a one-time\nprice with a negative amount. Includes tax when `tax_behavior` says so.\n","example":"50.00"}},"required":["archived","created_at","id","object","owner_source_id","plan_id","tax_behavior"],"type":"object"},"WebhookEvents":{"additionalProperties":false,"properties":{"events":{"description":"The provider's names for the events, as its webhook settings list them.","items":{"type":"string"},"type":"array"}},"required":["events"],"type":"object"},"Country":{"additionalProperties":false,"description":"A country, by its ISO 3166-1 alpha-2 code, as a customer's `country_code`\nholds it.\n","example":{"code":"US","name":"United States","object":"country"},"properties":{"code":{"description":"Its ISO 3166-1 alpha-2 code: `US`, `DK`.","example":"US","type":"string"},"name":{"description":"What it is called, in English: `United States`, `Denmark`.","example":"United States","type":"string"},"object":{"description":"The object's type, always `country`.","enum":["country"],"example":"country","type":"string"}},"required":["code","name","object"],"type":"object"},"IntegrationImport":{"additionalProperties":false,"description":"An integration import is one import of everything a provider integration holds, such\nas a Stripe account: when it started, how far it has got, and how it\nended. An integration runs one at a time, and the metrics that read its records\nare incomplete while it does.\n","example":{"ended_at":"2025-09-08T14:26:51Z","id":"019a0c0f-6b1d-7e3a-8c2f-4d5e6f7a8b9c","integration_id":"0198f1a3-0b7c-7e25-9f41-6a2d8c3e5b19","object":"source_import","progress":"1","started_at":"2025-09-08T14:10:06Z","status":"finished","step":null},"properties":{"ended_at":{"anyOf":[{"type":"null"},{"format":"date-time","type":"string"}],"description":"When the import finished, failed or was stopped, or empty while it runs. The integration's log says how it went.","example":"2025-09-08T14:26:51Z"},"id":{"description":"The import's unique identifier.","example":"019a0c0f-6b1d-7e3a-8c2f-4d5e6f7a8b9c","format":"uuid","type":"string"},"integration":{"anyOf":[{"type":"null"},{"allOf":[{"$ref":"#/components/schemas/Integration"}]}],"description":"The integration imported. Expandable.","example":{"connected_at":"2025-09-08T14:10:05Z","created_at":"2025-09-08T14:09:47Z","current_import_id":"019a0c0f-6b1d-7e3a-8c2f-4d5e6f7a8b9c","id":"0198f1a3-0b7c-7e25-9f41-6a2d8c3e5b19","imported_at":"2025-09-08T14:26:51Z","is_source":true,"name":"Stripe","object":"integration","priority":2,"project_id":"0198f1a2-6c52-7b18-8e3f-2a9c4d6b1e07","provider_account_id":"acct_1QfR3sT8uVw2XyZa","provider_code":"stripe","sandbox":false,"slug":"stripe","webhook_installed_at":"2025-09-08T14:10:06Z","webhook_url":"https://api.summerbase.com/webhooks/integrations/0198f1a3-0b7c-7e25-9f41-6a2d8c3e5b19?c=us1"}},"integration_id":{"description":"The integration imported.","example":"0198f1a3-0b7c-7e25-9f41-6a2d8c3e5b19","format":"uuid","type":"string"},"object":{"description":"The object's type, always `source_import`.","enum":["source_import"],"example":"source_import","type":"string"},"progress":{"anyOf":[{"type":"null"},{"format":"decimal","type":"string"}],"description":"How far the import has got, from 0 to 1, or empty while it does not\nknow: it cannot tell how much there is to read until it has read it.\n","example":"1"},"started_at":{"description":"When the import began.","example":"2025-09-08T14:10:06Z","format":"date-time","type":"string"},"status":{"description":"Where the import stands. While it is `running`, every metric that reads the integration's records is incomplete.","enum":["running","finished","failed","stopped"],"example":"finished","type":"string"},"step":{"anyOf":[{"type":"null"},{"type":"string"}],"description":"What the running import is doing, in a sentence: `Importing customers,\n1,240 of 8,100`. Empty once it has ended.\n"}},"required":["id","integration_id","object","started_at","status"],"type":"object"},"Field":{"additionalProperties":false,"description":"A field is a key a filter's comparison or a breakdown's dimension may name:\na field a record carries, a property a source wrote, or a dimension of the\nrun rate movements themselves. Which a query takes is the query's to say:\na measure lists them in its `filter_keys` and `dimensions`.\n","example":{"has_history":true,"id":"customer.first_paying_at","label":"First Paying At","object":"field","origin":"derived","promotes_to":null,"references":null,"source_slug":null,"subject_type":"customer","value_type":"datetime"},"properties":{"has_history":{"description":"Whether its value is kept as it changes, so that a comparison or a\ndimension naming it may say `at` — when the value is read: the end of\neach period, now, or a datetime. An ID, a plan's field and a dimension\nof the run rate movements themselves have none, and neither has a\nsubscription's currency, which is read from the revenue itself.\n","example":true,"type":"boolean"},"id":{"description":"The key, as a filter or a breakdown names it: `customer.id`, `customer.first_paying_at`,\n`subscription.properties.stripe.plan_tier`, `customer.external_ids.stripe`,\n`movement_type`.\n","example":"customer.first_paying_at","type":"string"},"label":{"description":"What to call it: `First Paying At`, `Plan`, or the name a property goes by.","example":"First Paying At","type":"string"},"object":{"description":"The object's type, always `field`.","enum":["field"],"example":"field","type":"string"},"origin":{"description":"Where its value comes from: `exclusive`, a field only the source a\nsubscription lives in may write; `promoted`, a customer's field any\nsource may write, taken from the highest-ranked source that has one;\n`derived`, one Summerbase works out; `property`, what one source has\nwritten about the record; `identity`, an ID for the record — its own\n`id`, which a filter matches by any of its IDs, `stripe:cus_123` as\nwell as its Summerbase ID, or what one source calls it; or\n`run_rate_movement`, a dimension of the revenue movements themselves.\n","enum":["exclusive","promoted","derived","property","identity","run_rate_movement"],"example":"derived","type":"string"},"promotes_to":{"anyOf":[{"type":"null"},{"type":"string"}],"description":"For a property that is one source's say in a promoted field, the key of\nthat field, which carries the value resolved across sources:\n`customer.properties.stripe.name` is what Stripe said, and its\n`promotes_to`, `customer.name`, is the answer. Absent otherwise.\n"},"references":{"anyOf":[{"type":"null"},{"enum":["country","currency","plan","price","source"],"type":"string"}],"description":"What each value is the id of, for a field whose values name something:\n`country` for a country code, `currency` for a currency code, `plan`\nfor a plan id. Absent for a field holding values of its own.\n"},"source_slug":{"anyOf":[{"type":"null"},{"type":"string"}],"description":"For a property or a source's ID, the source that wrote it. Absent otherwise."},"subject_type":{"description":"What it is about: `customer`, `subscription`, `subscription_item`, `plan`,\nor `run_rate_movement` for a dimension of the run rate movements themselves.\n","example":"customer","type":"string"},"value_type":{"description":"What it holds: `string`, `number`, `boolean`, `datetime`, or\n`string_list` — any number of strings, zero, one or several. A\nbreakdown by a `datetime` field says which unit to `truncate` to, and\none by a `number` may give `boundaries`, or ask for them with `auto`.\nA filter compares a `string_list` with `includes`, which holds when one\nof its values is the value, and with nothing else; `not` around it\nholds when none is.\n","example":"datetime","type":"string"}},"required":["has_history","id","label","object","origin","subject_type","value_type"],"type":"object"},"CreateSubscriptionItemResult":{"additionalProperties":false,"description":"A subscription item is one price on a subscription: a price, a quantity and a\nunit amount, billed every interval. Its plan is its price's. A subscription's amounts are summed from\nits items. Items are usually written with their subscription, as its\n`items`, and can also be changed one at a time.\n","example":{"created_at":"2025-11-17T10:04:03Z","deleted":false,"discount_amount":"0","discount_ends_at":null,"id":"0199a7c2-5b41-7c62-9d3e-6a1f8b2c4d97","identities":["stripe:si_QhT6vW8xY0zA3b"],"interval":"month","interval_count":1,"net_amount":"1250.00","object":"subscription_item","plan_id":"0198f1a4-1d2e-7a40-9b31-5c6d7e8f9a0b","plan_name":"Growth","price_id":"0198f1a4-2e5f-7b80-a1c2-3d4e5f6a7b8c","price_name":"Growth monthly","pricing_model":"fixed","properties":{"seat_type":"standard"},"properties_by_source":{"backend":{"seat_type":"standard"}},"quantity":"25","subscription_id":"0199a7c2-5b3e-7a14-8c6d-2f9e4b7a1c05","unit_amount":"50.00"},"properties":{"created_at":{"description":"When this item was first recorded.","example":"2025-11-17T10:04:03Z","format":"date-time","type":"string"},"deleted":{"description":"Whether the item has come off the subscription. **When** it did is the\n`effective_at` of the write that set this.\n","example":false,"type":"boolean"},"discount_amount":{"description":"What comes off this item's invoice, in the subscription's currency,\nafter every discount the source applies to it has been worked out:\ncoupons at any level, stacked in the source's order, restricted to\nthe products they cover. The item is worth `unit_amount × quantity`\nless this, each interval. Zero is no discount.\n","example":"0","format":"decimal","type":"string"},"discount_ends_at":{"anyOf":[{"type":"null"},{"format":"date-time","type":"string"}],"description":"When the discount, or the earliest part of it, stops applying, if the\nsource knows. Information for a reader: the discount comes off when the\nsource writes a smaller `discount_amount`, not when this passes.\n"},"id":{"description":"The item's Summerbase id. Anywhere an item is addressed, one of its `identities` works too.","example":"0199a7c2-5b41-7c62-9d3e-6a1f8b2c4d97","format":"uuid","type":"string"},"identities":{"anyOf":[{"type":"null"},{"items":{"type":"string"},"type":"array"}],"description":"What each source calls this subscription item, as `source:id`. Any entry addresses it — `stripe:cus_123`.","example":["stripe:si_QhT6vW8xY0zA3b"]},"interval":{"description":"The unit this item bills in.","enum":["day","week","month","year"],"example":"month","type":"string"},"interval_count":{"description":"How many intervals between charges.","example":1,"minimum":1,"type":"integer"},"net_amount":{"anyOf":[{"type":"null"},{"format":"decimal","type":"string"}],"description":"What this item comes to each time it is billed, in its subscription's\ncurrency: `unit_amount` times `quantity`, less `discount_amount`.\n","example":"1250.00"},"object":{"description":"The object's type, always `subscription_item`.","enum":["subscription_item"],"example":"subscription_item","type":"string"},"plan_id":{"anyOf":[{"type":"null"},{"format":"uuid","type":"string"}],"description":"The plan this item is on: its price's.","example":"0198f1a4-1d2e-7a40-9b31-5c6d7e8f9a0b"},"plan_name":{"anyOf":[{"type":"null"},{"type":"string"}],"description":"What the plan this item is on is called: its price's.","example":"Growth"},"price":{"anyOf":[{"type":"null"},{"allOf":[{"$ref":"#/components/schemas/Price"}]}],"description":"The price this item is on, when a source named one. Expandable.","example":{"archived":false,"created_at":"2025-09-08T14:12:30Z","currency_code":"USD","id":"0198f1a4-2e5f-7b80-a1c2-3d4e5f6a7b8c","identities":["stripe:price_1PqR8sT2uV4wX6yZ"],"interval":"month","interval_count":1,"name":"Growth monthly","object":"price","owner_source_id":"0198f1a3-0b7c-7e25-9f41-6a2d8c3e5b19","plan_id":"0198f1a4-1d2e-7a40-9b31-5c6d7e8f9a0b","pricing_model":"fixed","properties":{"tier":"mid"},"properties_by_source":{"backend":{"tier":"mid"}},"tax_behavior":"exclusive","unit_amount":"50.00"}},"price_id":{"anyOf":[{"type":"null"},{"format":"uuid","type":"string"}],"description":"The price this item is on, when a source named one.","example":"0198f1a4-2e5f-7b80-a1c2-3d4e5f6a7b8c"},"price_name":{"anyOf":[{"type":"null"},{"type":"string"}],"description":"What the price this item is on is called, when a source named one.","example":"Growth monthly"},"pricing_model":{"description":"How the unit amount is arrived at.","enum":["fixed","tiered","metered"],"example":"fixed","type":"string"},"properties":{"anyOf":[{"type":"null"},{"additionalProperties":{"anyOf":[{"type":"string"},{"type":"number"},{"type":"boolean"}]},"type":"object"}],"description":"**Your** source's properties on this subscription item, as they stand now. What you write here is what you read back — another source's values never appear in it and can never be overwritten through it. `properties_by_source` has everyone's. When you have written a key through several of this subscription item's ids, its value is the most recent change any of them made.","example":{"seat_type":"standard"}},"properties_by_source":{"anyOf":[{"type":"null"},{"additionalProperties":{"additionalProperties":{"anyOf":[{"type":"string"},{"type":"number"},{"type":"boolean"}]},"description":"Property keys and their current values.","type":"object"},"type":"object"}],"description":"Every source's properties on this subscription item, keyed by source slug — including your own. Read-only: a write only ever reaches the source it is attributed to.","example":{"backend":{"seat_type":"standard"}}},"quantity":{"description":"How many units at this unit amount. A decimal, because a unit is not\nalways a whole seat: a billing system that meters usage sends 1.5 of\nsomething, and rounding it would misstate what the item is worth.\n","example":"25","format":"decimal","type":"string"},"response_metadata":{"anyOf":[{"type":"null"},{"additionalProperties":false,"properties":{"warnings":{"description":"Parts of this write that were skipped — a property whose value did not\nfit its type — and things it did that were worth saying, like merging\ntwo customers. The rest of the write landed. Absent when there is\nnothing to report.\n\nSend `on_warning: \"error\"` to have these refuse the write instead,\nand come back as `error.errors` with the same codes.\n","items":{"$ref":"#/components/schemas/Warning"},"type":"array"}},"type":"object"}],"description":"About this request rather than about the record. Absent when there is nothing to say."},"subscription":{"anyOf":[{"type":"null"},{"allOf":[{"$ref":"#/components/schemas/Subscription"}]}],"description":"The subscription this is an item of. Expandable.","example":{"annual_amount":"15000.00","billing_amounts":[{"interval":"month","interval_count":1,"net_amount":"1250.00"}],"currency_code":"USD","current_period_ends_at":"2026-11-01T10:04:00Z","customer_id":"0199a7c2-4e1b-7f38-b9d2-5a6c8e0f1d73","id":"0199a7c2-5b3e-7a14-8c6d-2f9e4b7a1c05","identities":["stripe:sub_1QhT5uV7wX9yZ2aB"],"monthly_amount":"1250.00","object":"subscription","owner_source_id":"0198f1a3-0b7c-7e25-9f41-6a2d8c3e5b19","properties":{"account_manager":"Priya Shah"},"properties_by_source":{"backend":{"account_manager":"Priya Shah"},"stripe":{"contract_id":"C-2025-118"}},"recorded_at":"2025-11-17T10:04:03Z","started_at":"2025-11-17T10:04:00Z","status":"active","trial_ends_at":"2025-12-01T10:04:00Z"}},"subscription_id":{"description":"The subscription this is an item of.","example":"0199a7c2-5b3e-7a14-8c6d-2f9e4b7a1c05","format":"uuid","type":"string"},"unit_amount":{"description":"What one unit costs per interval. Excludes tax and any discount.","example":"50.00","format":"decimal","type":"string"}},"required":["created_at","deleted","discount_amount","id","interval","interval_count","object","pricing_model","quantity","subscription_id","unit_amount"],"type":"object"},"Currency":{"additionalProperties":false,"description":"A currency Summerbase holds exchange rates for, by its ISO 4217 code: what a\nsubscription may bill in and a project may report in.\n","example":{"code":"USD","name":"US dollar","object":"currency"},"properties":{"code":{"description":"Its ISO 4217 code — `USD`, `EUR`, `DKK`.","example":"USD","maxLength":3,"minLength":3,"type":"string"},"name":{"description":"What it is called, in English: `US dollar`, `Danish krone`.","example":"US dollar","maxLength":100,"type":"string"},"object":{"description":"The object's type, always `currency`.","enum":["currency"],"example":"currency","type":"string"}},"required":["code","name","object"],"type":"object"},"DuplicatePlans":{"additionalProperties":false,"description":"A group of duplicate plans is plans that match by the pair of fields you\nsearched by, and so may be one plan known more than once — the same plan\nin two billing systems, or in two accounts of one. It is a suggestion:\nlook it over, and merge what is one with `POST /v1/plans/{id}/merge`.\n","example":{"matches":[{"plan_ids":["0198f1a4-1d2e-7a40-9b31-5c6d7e8f9a0b","019a3f10-9c3d-7e5f-8a7b-2c3d4e5f6a71"],"value":"Growth"}],"object":"duplicate_plans","plan_count":2,"plan_id":"0198f1a4-1d2e-7a40-9b31-5c6d7e8f9a0b","plan_ids":["0198f1a4-1d2e-7a40-9b31-5c6d7e8f9a0b","019a3f10-9c3d-7e5f-8a7b-2c3d4e5f6a71"]},"properties":{"matches":{"description":"Each value that links plans of `plan_ids`, and which ones hold it.","example":[{"plan_ids":["0198f1a4-1d2e-7a40-9b31-5c6d7e8f9a0b","019a3f10-9c3d-7e5f-8a7b-2c3d4e5f6a71"],"value":"Growth"}],"items":{"additionalProperties":false,"properties":{"plan_ids":{"description":"The plans of `plan_ids` holding it, oldest first.","items":{"format":"uuid","type":"string"},"type":"array"},"value":{"description":"The value they share, as one of them holds it.","type":"string"}},"required":["plan_ids","value"],"type":"object"},"type":"array"},"object":{"description":"The object's type, always `duplicate_plans`.","enum":["duplicate_plans"],"example":"duplicate_plans","type":"string"},"plan_count":{"description":"How many plans the group has, `plan_ids` and any beyond them.","example":2,"type":"integer"},"plan_id":{"description":"The group's oldest plan, first in `plan_ids`: the one to merge the\nothers into unless you have a reason to keep another.\n","example":"0198f1a4-1d2e-7a40-9b31-5c6d7e8f9a0b","format":"uuid","type":"string"},"plan_ids":{"description":"The group's ten oldest plans, oldest first by when they were first seen.\nMerge them, and the rest of a larger group match the one kept and come\nback on the next search.\n","example":["0198f1a4-1d2e-7a40-9b31-5c6d7e8f9a0b","019a3f10-9c3d-7e5f-8a7b-2c3d4e5f6a71"],"items":{"format":"uuid","type":"string"},"type":"array"},"plans":{"description":"The plans of `plan_ids`, as records, in the same order.","example":[{"archived":false,"created_at":"2025-09-08T14:12:30Z","id":"0198f1a4-1d2e-7a40-9b31-5c6d7e8f9a0b","identities":["stripe:prod_QhR2sT4uV6wX8y"],"name":"Growth","object":"plan","price_count":2,"properties":{"tier":"mid"},"properties_by_source":{"backend":{"tier":"mid"}}}],"items":{"$ref":"#/components/schemas/Plan"},"type":"array"}},"required":["matches","object","plan_count","plan_id","plan_ids","plans"],"type":"object"},"CreateSubscriptionResult":{"additionalProperties":false,"description":"A subscription is what a customer is subscribed to, in the one source it\nlives in. It is made of items that carry the prices, and its\n`monthly_amount` is what they are worth a month; a customer's `mrr` is the\nsum over their subscriptions.\n","example":{"annual_amount":"15000.00","billing_amounts":[{"interval":"month","interval_count":1,"net_amount":"1250.00"}],"cancels_at":null,"currency_code":"USD","current_period_ends_at":"2026-11-01T10:04:00Z","customer_id":"0199a7c2-4e1b-7f38-b9d2-5a6c8e0f1d73","ended_at":null,"id":"0199a7c2-5b3e-7a14-8c6d-2f9e4b7a1c05","identities":["stripe:sub_1QhT5uV7wX9yZ2aB"],"monthly_amount":"1250.00","object":"subscription","owner_source_id":"0198f1a3-0b7c-7e25-9f41-6a2d8c3e5b19","properties":{"account_manager":"Priya Shah"},"properties_by_source":{"backend":{"account_manager":"Priya Shah"},"stripe":{"contract_id":"C-2025-118"}},"recorded_at":"2025-11-17T10:04:03Z","started_at":"2025-11-17T10:04:00Z","status":"active","trial_ends_at":"2025-12-01T10:04:00Z"},"properties":{"annual_amount":{"anyOf":[{"type":"null"},{"format":"decimal","type":"string"}],"description":"What this subscription is worth a year, in `currency_code`:\n`monthly_amount` times twelve.\n","example":"15000.00"},"billing_amounts":{"anyOf":[{"type":"null"},{"items":{"$ref":"#/components/schemas/SubscriptionBillingAmount"},"type":"array"}],"description":"What this subscription comes to each time it bills: the items still on\nit, their `net_amount` summed per billing interval, longest interval\nfirst. One entry for nearly every subscription; one per interval for a\nsubscription whose items bill on different ones, such as a yearly fee\nbeside a monthly one. Its price whatever its status — a trial still has\none. `monthly_amount` is what it is worth on one footing.\n","example":[{"interval":"month","interval_count":1,"net_amount":"1250.00"}]},"cancels_at":{"anyOf":[{"type":"null"},{"format":"date-time","type":"string"}],"description":"A cancellation already booked. It is still in service until then, and\nthe source says so with `status: canceled` when it stops.\n"},"currency":{"anyOf":[{"type":"null"},{"allOf":[{"$ref":"#/components/schemas/Currency"}]}],"description":"The currency it bills in. Expandable.","example":{"code":"USD","name":"US dollar","object":"currency"}},"currency_code":{"description":"The currency it bills in, by its ISO 4217 code.","example":"USD","maxLength":3,"minLength":3,"type":"string"},"current_period_ends_at":{"anyOf":[{"type":"null"},{"format":"date-time","type":"string"}],"description":"When the current billing period ends. The next charge is due then,\nunless a cancellation is booked for it.\n","example":"2026-11-01T10:04:00Z"},"customer":{"anyOf":[{"type":"null"},{"allOf":[{"$ref":"#/components/schemas/Customer"}]}],"description":"The customer who is subscribed. Expandable.","example":{"arr":"15000.00","country_code":"US","created_at":"2025-11-03T16:20:04Z","email":"billing@northwind.example.com","first_paying_at":"2025-12-01T10:04:00Z","first_seen_at":"2025-11-03T16:20:00Z","first_subscribed_at":"2025-11-17T10:04:00Z","id":"0199a7c2-4e1b-7f38-b9d2-5a6c8e0f1d73","identities":["stripe:cus_QX7bN2kLp4Rt9v","backend:acct_4821"],"mrr":"1250.00","name":"Northwind Labs","object":"customer","properties":{"seats":25},"properties_by_source":{"backend":{"seats":25},"stripe":{"email":"billing@northwind.example.com","name":"Northwind Labs"}},"signed_up_at":"2025-11-03T16:20:00Z","status":"paying"}},"customer_id":{"description":"The customer who is subscribed.","example":"0199a7c2-4e1b-7f38-b9d2-5a6c8e0f1d73","format":"uuid","type":"string"},"ended_at":{"anyOf":[{"type":"null"},{"format":"date-time","type":"string"}],"description":"When service stopped, beside `status: canceled`. Clear it, with `status`\nback to `active`, to resume a subscription; the same id may be brought\nback as often as the source does so.\n"},"id":{"description":"The subscription's Summerbase id. Anywhere a subscription is addressed, one of its `identities` works too.","example":"0199a7c2-5b3e-7a14-8c6d-2f9e4b7a1c05","format":"uuid","type":"string"},"identities":{"anyOf":[{"type":"null"},{"items":{"type":"string"},"type":"array"}],"description":"What each source calls this subscription, as `source:id`. Any entry addresses it — `stripe:cus_123`.","example":["stripe:sub_1QhT5uV7wX9yZ2aB"]},"items":{"anyOf":[{"type":"null"},{"items":{"$ref":"#/components/schemas/SubscriptionItem"},"type":"array"}],"description":"The items it is made of, and where the money is. Expandable.","example":[{"created_at":"2025-11-17T10:04:03Z","deleted":false,"discount_amount":"0","id":"0199a7c2-5b41-7c62-9d3e-6a1f8b2c4d97","identities":["stripe:si_QhT6vW8xY0zA3b"],"interval":"month","interval_count":1,"net_amount":"1250.00","object":"subscription_item","plan_id":"0198f1a4-1d2e-7a40-9b31-5c6d7e8f9a0b","plan_name":"Growth","price_id":"0198f1a4-2e5f-7b80-a1c2-3d4e5f6a7b8c","price_name":"Growth monthly","pricing_model":"fixed","properties":{"seat_type":"standard"},"properties_by_source":{"backend":{"seat_type":"standard"}},"quantity":"25","subscription_id":"0199a7c2-5b3e-7a14-8c6d-2f9e4b7a1c05","unit_amount":"50.00"}]},"monthly_amount":{"description":"What this subscription is worth a month, in `currency_code`, after the\nitems' discounts — **derived, not reported.** The sum of what its items\nare worth; zero while it is on trial, paused or ended, and zero on a\nfree plan. A customer's `mrr` is these summed across their subscriptions, in\nthe reporting currency.\n","example":"1250.00","format":"decimal","type":"string"},"object":{"description":"The object's type, always `subscription`.","enum":["subscription"],"example":"subscription","type":"string"},"owner_source":{"anyOf":[{"type":"null"},{"allOf":[{"$ref":"#/components/schemas/Integration"}]}],"description":"The source this subscription lives in — the only one that may write its\nown fields. Any source may attach properties and identities to it.\n Expandable.","example":{"connected_at":"2025-09-08T14:10:05Z","created_at":"2025-09-08T14:09:47Z","current_import_id":"019a0c0f-6b1d-7e3a-8c2f-4d5e6f7a8b9c","id":"0198f1a3-0b7c-7e25-9f41-6a2d8c3e5b19","imported_at":"2025-09-08T14:26:51Z","is_source":true,"name":"Stripe","object":"integration","priority":2,"project_id":"0198f1a2-6c52-7b18-8e3f-2a9c4d6b1e07","provider_account_id":"acct_1QfR3sT8uVw2XyZa","provider_code":"stripe","sandbox":false,"slug":"stripe","webhook_installed_at":"2025-09-08T14:10:06Z","webhook_url":"https://api.summerbase.com/webhooks/integrations/0198f1a3-0b7c-7e25-9f41-6a2d8c3e5b19?c=us1"}},"owner_source_id":{"description":"The source this subscription lives in — the only one that may write its\nown fields. Any source may attach properties and identities to it.\n","example":"0198f1a3-0b7c-7e25-9f41-6a2d8c3e5b19","format":"uuid","type":"string"},"properties":{"anyOf":[{"type":"null"},{"additionalProperties":{"anyOf":[{"type":"string"},{"type":"number"},{"type":"boolean"}]},"type":"object"}],"description":"**Your** source's properties on this subscription, as they stand now. What you write here is what you read back — another source's values never appear in it and can never be overwritten through it. `properties_by_source` has everyone's. When you have written a key through several of this subscription's ids, its value is the most recent change any of them made.","example":{"account_manager":"Priya Shah"}},"properties_by_source":{"anyOf":[{"type":"null"},{"additionalProperties":{"additionalProperties":{"anyOf":[{"type":"string"},{"type":"number"},{"type":"boolean"}]},"description":"Property keys and their current values.","type":"object"},"type":"object"}],"description":"Every source's properties on this subscription, keyed by source slug — including your own. Read-only: a write only ever reaches the source it is attributed to.","example":{"backend":{"account_manager":"Priya Shah"},"stripe":{"contract_id":"C-2025-118"}}},"recorded_at":{"description":"When Summerbase first recorded it. Never set by a caller.","example":"2025-11-17T10:04:03Z","format":"date-time","type":"string"},"response_metadata":{"anyOf":[{"type":"null"},{"additionalProperties":false,"properties":{"warnings":{"description":"Parts of this write that were skipped — a property whose value did not\nfit its type — and things it did that were worth saying, like merging\ntwo customers. The rest of the write landed. Absent when there is\nnothing to report.\n\nSend `on_warning: \"error\"` to have these refuse the write instead,\nand come back as `error.errors` with the same codes.\n","items":{"$ref":"#/components/schemas/Warning"},"type":"array"}},"type":"object"}],"description":"About this request rather than about the record. Absent when there is nothing to say."},"started_at":{"description":"When the subscription began — **derived, not reported.**\n\nIt is the earliest instant anything was asserted about the subscription or\nits items. A source cannot say it began earlier than it described it: send\nthe earliest write with `effective_at` at the real start instead, which is\nwhat an import of history does anyway.\n\nIt moves earlier when a later write describes an earlier moment.\n","example":"2025-11-17T10:04:00Z","format":"date-time","type":"string"},"status":{"description":"Where it is in its life. Not whether it has been paid for.","enum":["pending","trialing","active","paused","canceled"],"example":"active","type":"string"},"trial_ends_at":{"anyOf":[{"type":"null"},{"format":"date-time","type":"string"}],"description":"When the trial ends, or ended. A subscription that trials starts\ntrialing, so when it began is `started_at` and its status history.\n","example":"2025-12-01T10:04:00Z"}},"required":["currency_code","customer_id","id","monthly_amount","object","owner_source_id","recorded_at","started_at","status"],"type":"object"},"Integration":{"additionalProperties":false,"description":"An integration is a system the project is connected to: a billing\nprovider such as Stripe, which Summerbase fetches from itself, your own\nbackend, which writes through the API, or Summerbase, where what your\nteam writes by hand is kept.\n\nAn integration that sends Summerbase data is a **source**, and\n`is_source` says whether it is one. Every customer, subscription and plan is\nidentified within a source, as `stripe:cus_123`; everything an API key\nwrites is attributed to the key's source; and wherever a record names a\nsource — `owner_source_id`, `properties_by_source` — it names one of these.\nEvery provider there is today is a source.\n","example":{"connected_at":"2025-09-08T14:10:05Z","created_at":"2025-09-08T14:09:47Z","current_import_id":"019a0c0f-6b1d-7e3a-8c2f-4d5e6f7a8b9c","id":"0198f1a3-0b7c-7e25-9f41-6a2d8c3e5b19","imported_at":"2025-09-08T14:26:51Z","is_source":true,"name":"Stripe","object":"integration","priority":2,"project_id":"0198f1a2-6c52-7b18-8e3f-2a9c4d6b1e07","provider_account_id":"acct_1QfR3sT8uVw2XyZa","provider_code":"stripe","sandbox":false,"slug":"stripe","webhook_installed_at":"2025-09-08T14:10:06Z","webhook_url":"https://api.summerbase.com/webhooks/integrations/0198f1a3-0b7c-7e25-9f41-6a2d8c3e5b19?c=us1"},"properties":{"connected_at":{"anyOf":[{"type":"null"},{"format":"date-time","type":"string"}],"description":"When a provider integration was given its credential, or empty: a provider\nintegration that has not been connected yet or has been disconnected, and\nevery integration that is not a provider's.\n","example":"2025-09-08T14:10:05Z"},"created_at":{"description":"When the integration was added.","example":"2025-09-08T14:09:47Z","format":"date-time","type":"string"},"current_import":{"anyOf":[{"type":"null"},{"allOf":[{"$ref":"#/components/schemas/IntegrationImport"}]}],"description":"The integration's import: the one running now, or the one that ran last,\nwhose `status` says how it ended. Empty when the integration has never\nimported.\n Expandable.","example":{"ended_at":"2025-09-08T14:26:51Z","id":"019a0c0f-6b1d-7e3a-8c2f-4d5e6f7a8b9c","integration_id":"0198f1a3-0b7c-7e25-9f41-6a2d8c3e5b19","object":"source_import","progress":"1","started_at":"2025-09-08T14:10:06Z","status":"finished"}},"current_import_id":{"anyOf":[{"type":"null"},{"format":"uuid","type":"string"}],"description":"The integration's import: the one running now, or the one that ran last,\nwhose `status` says how it ended. Empty when the integration has never\nimported.\n","example":"019a0c0f-6b1d-7e3a-8c2f-4d5e6f7a8b9c"},"id":{"description":"The integration's unique identifier.","example":"0198f1a3-0b7c-7e25-9f41-6a2d8c3e5b19","format":"uuid","type":"string"},"imported_at":{"anyOf":[{"type":"null"},{"format":"date-time","type":"string"}],"description":"When the last import finished, or empty when none has.","example":"2025-09-08T14:26:51Z"},"is_source":{"anyOf":[{"type":"null"},{"type":"boolean"}],"description":"Whether the integration sends Summerbase data, and so has a `slug` and a\n`priority` and can own records. Fixed by its provider.\n","example":true},"name":{"description":"What the integration is called, for people reading a screen.","example":"Stripe","maxLength":255,"minLength":1,"type":"string"},"object":{"description":"The object's type, always `integration`.","enum":["integration"],"example":"integration","type":"string"},"priority":{"anyOf":[{"type":"null"},{"type":"integer"}],"description":"Where this source sits when resolving `name` and `email`. Lower wins, and\nthe Summerbase integration starts at the top so a manual correction\noutranks a provider sync. It orders nothing else — every other property\nis namespaced by source and never arbitrated. Defaults to 10 for a\n`custom` integration and 100 for a provider's. Null for an integration\nthat is not a source.\n","example":2},"project_id":{"description":"The project the integration belongs to.","example":"0198f1a2-6c52-7b18-8e3f-2a9c4d6b1e07","format":"uuid","type":"string"},"provider_account_id":{"anyOf":[{"type":"null"},{"type":"string"}],"description":"The provider's own id for the account the integration is connected to:\n`acct_…` for Stripe. Kept through a disconnect, so that connecting\nagain is checked against it.\n","example":"acct_1QfR3sT8uVw2XyZa"},"provider_code":{"description":"The code of the system the integration connects to. `custom` is one you\nwrite to yourself, and the only one that accepts writes; `stripe` is\nconnected with `connect_stripe` and fetches for itself.\n","enum":["custom","stripe","summerbase"],"example":"stripe","type":"string"},"sandbox":{"anyOf":[{"type":"null"},{"type":"boolean"}],"description":"Whether the connection is to the provider's test mode rather than a\nlive account; empty while nothing is connected.\n","example":false},"slug":{"anyOf":[{"type":"null"},{"maxLength":100,"minLength":1,"type":"string"}],"description":"How a source's records are identified — the `stripe` in `stripe:cus_123`.\nLowercase letters, digits, hyphens and underscores; unique within the\nproject and **fixed once the integration exists**, because your code\nhard-codes it. A reference shaped like an id is read as one, so a slug\nmay not look like a UUID. Null for an integration that is not a source.\n","example":"stripe"},"webhook_installed_at":{"anyOf":[{"type":"null"},{"format":"date-time","type":"string"}],"description":"When the provider's webhook was installed for this integration — by the\nconnection, or by hand and registered with `register_stripe_webhook` —\nor empty: nothing is connected, or the integration is connected without a\nwebhook and its changes arrive with the hourly reconcile.\n","example":"2025-09-08T14:10:06Z"},"webhook_url":{"anyOf":[{"type":"null"},{"type":"string"}],"description":"Where the provider posts this integration's events: the address of the\nendpoint a connection installs, and of one to install by hand. Empty\nfor an integration whose provider posts no webhooks.\n","example":"https://api.summerbase.com/webhooks/integrations/0198f1a3-0b7c-7e25-9f41-6a2d8c3e5b19?c=us1"}},"required":["created_at","id","name","object","project_id","provider_code"],"type":"object"},"Error":{"description":"The body of any failed request.","properties":{"error":{"properties":{"code":{"description":"What went wrong, stable enough to branch on.","type":"string"},"errors":{"anyOf":[{"type":"null"},{"description":"Present only when more than one thing was wrong. The fields above mirror the first.","items":{"properties":{"code":{"description":"What went wrong, stable enough to branch on.","type":"string"},"message":{"description":"A message safe to show a user.","type":"string"},"param":{"description":"The input at fault. Absent when the failure is not about one.","type":"string"},"type":{"description":"The class of failure.","enum":["invalid_request_error","authentication_error","permission_error","rate_limit_error","api_error"],"type":"string"}},"required":["type","code","message"],"type":"object"},"type":"array"}]},"message":{"description":"A message safe to show a user.","type":"string"},"param":{"description":"The input at fault. Absent when the failure is not about one.","type":"string"},"type":{"description":"The class of failure.","enum":["invalid_request_error","authentication_error","permission_error","rate_limit_error","api_error"],"type":"string"}},"required":["type","code","message"],"type":"object"}},"required":["error"],"type":"object"},"ApiKey":{"additionalProperties":false,"description":"An API key lets a program call the Summerbase API on behalf of one\nproject, with a role in it. A key that writes is bound to a source, which\neverything it writes is attributed to. Its secret is shown once, when it is\ncreated.\n","example":{"created_at":"2026-02-11T09:15:42Z","id":"0199b2d3-8e4f-7a61-b2c3-7d9e1f4a6c85","last_used_at":"2026-10-19T08:41:00Z","name":"Billing sync","object":"api_key","prefix":"sb_us1_k7hq2mxa","project_id":"0198f1a2-6c52-7b18-8e3f-2a9c4d6b1e07","project_role":"writer","source_id":"0198f1a3-0c18-7d93-a6e2-1b4f9d7c3a58"},"properties":{"created_at":{"description":"When the key was issued.","example":"2026-02-11T09:15:42Z","format":"date-time","type":"string"},"id":{"description":"The API key's unique identifier. Not its secret.","example":"0199b2d3-8e4f-7a61-b2c3-7d9e1f4a6c85","format":"uuid","type":"string"},"last_used_at":{"anyOf":[{"type":"null"},{"format":"date-time","type":"string"}],"description":"When the key was last used, to the nearest minute, or null if never.","example":"2026-10-19T08:41:00Z"},"name":{"description":"What the key is for, so it can be recognized in a list.","example":"Billing sync","maxLength":255,"minLength":1,"type":"string"},"object":{"description":"The object's type, always `api_key`.","enum":["api_key"],"example":"api_key","type":"string"},"prefix":{"description":"The start of the secret, which is not itself secret. Enough to tell two\nkeys apart in a list.\n","example":"sb_us1_k7hq2mxa","type":"string"},"project_id":{"description":"The project the key is scoped to.","example":"0198f1a2-6c52-7b18-8e3f-2a9c4d6b1e07","format":"uuid","type":"string"},"project_role":{"description":"What the key may do in its project. `writer` can send data as well as read\nit, which is what an SDK needs; `viewer` can only read.\n","enum":["viewer","writer","admin","owner"],"example":"writer","type":"string"},"source":{"anyOf":[{"type":"null"},{"allOf":[{"$ref":"#/components/schemas/Integration"}]}],"description":"The custom integration everything this key writes is attributed to. Required\nfor a `writer`; a `viewer` may leave it unset and reads `properties` as\nthe Summerbase integration.\n Expandable.","example":{"connected_at":"2025-09-08T14:10:05Z","created_at":"2025-09-08T14:09:47Z","current_import_id":"019a0c0f-6b1d-7e3a-8c2f-4d5e6f7a8b9c","id":"0198f1a3-0b7c-7e25-9f41-6a2d8c3e5b19","imported_at":"2025-09-08T14:26:51Z","is_source":true,"name":"Stripe","object":"integration","priority":2,"project_id":"0198f1a2-6c52-7b18-8e3f-2a9c4d6b1e07","provider_account_id":"acct_1QfR3sT8uVw2XyZa","provider_code":"stripe","sandbox":false,"slug":"stripe","webhook_installed_at":"2025-09-08T14:10:06Z","webhook_url":"https://api.summerbase.com/webhooks/integrations/0198f1a3-0b7c-7e25-9f41-6a2d8c3e5b19?c=us1"}},"source_id":{"anyOf":[{"type":"null"},{"format":"uuid","type":"string"}],"description":"The custom integration everything this key writes is attributed to. Required\nfor a `writer`; a `viewer` may leave it unset and reads `properties` as\nthe Summerbase integration.\n","example":"0198f1a3-0c18-7d93-a6e2-1b4f9d7c3a58"}},"required":["created_at","id","name","object","prefix","project_id","project_role"],"type":"object"},"MetricQuery":{"additionalProperties":false,"properties":{"breakdown":{"anyOf":[{"type":"null"},{"items":{"anyOf":[{"description":"A key: `customer.country_code`, `subscription_item.plan_id`, `movement_type`, `revenue_type`.","type":"string"},{"additionalProperties":false,"properties":{"at":{"description":"When a record's value is read: `window`, as things stood at the time over each window — a level by the value at its end, a movement by the value when the money moved, a cohort by the value at its start — unless said; `now`, so every period is split by the value held now; an instant relative to it, `now-3M`; a date, read as the end of that day in the reporting timezone; or a datetime, ISO 8601, UTC when it names no zone. The same `at` a filter comparison takes. Not for the run rate movements' own dimensions.","type":"string"},"boundaries":{"anyOf":[{"items":{"type":"number"},"maxItems":20,"minItems":1,"type":"array"},{"enum":["auto"],"type":"string"}],"description":"For a key that holds a number: up to 20 numbers, each above the one before and each starting a category — under the first, first to second, …, last and over. Or `auto`, and the query chooses them: up to five ranges holding about as many records each, at round numbers, from the values the records hold — which is how to ask for a distribution without knowing the values. The answer's `query` carries the numbers chosen, to send back for the same ranges; `auto` again follows the values."},"key":{"description":"The key, named as a filter names it.","type":"string"},"max_categories":{"description":"At most how many categories this dimension answers with, `Other` counted among them: the largest by the first measure carried at this depth in the last period are kept, and the rest are folded into `Other`, always last. 1 to 50, 10 unless said. A dimension with a fixed set of categories keeps them all; one over dates keeps every period up to 50, oldest first, folding older ones into `Before …`; one with `boundaries` has one per range.","maximum":50,"minimum":1,"type":"integer"},"measure":{"anyOf":[{"type":"string"},{"items":{"type":"string"},"minItems":1,"type":"array"}],"description":"Which measures the nodes along this dimension carry: a key, or a list of them. The root's `measure` unless said, less any this dimension cannot carry."},"truncate":{"description":"For a key that holds a datetime: the unit its values are cut to, in the reporting timezone. Each category is one such unit, and the categories come in order, newest kept and older folded into `Before …`.","enum":["week","month","quarter","year"],"type":"string"}},"required":["key"],"type":"object"}]},"maxItems":3,"type":"array"}],"description":"Up to three dimensions to split each value by, such as\n`[\"subscription_item.plan_id\"]`. Each is a key, or an object that\nsays more about it.\n"},"currency_code":{"anyOf":[{"type":"null"},{"maxLength":3,"minLength":3,"type":"string"}],"description":"The currency to report in, by ISO 4217 code: `EUR`. The project's\nreporting currency unless said.\n"},"filter":{"anyOf":[{"type":"null"},{"allOf":[{"$ref":"#/components/schemas/FilterCondition"}]}],"description":"Counts only the customers, subscriptions or items the condition\nmatches. See [Filters](/docs/api/filters) for how to write one.\n"},"from":{"anyOf":[{"type":"null"},{"type":"string"}],"description":"The start of the range: a date, `2026-01-01`, or a date relative to\ntoday, `now-12M/M`, or `beginning`, when your first revenue moved.\nUnless said, the start of this month with no interval, and a run of\nwhole periods with one: twelve months back by the month.\n"},"interval":{"anyOf":[{"type":"null"},{"enum":["day","week","month","quarter","year"],"type":"string"}],"description":"Cuts the range into periods for `timeseries`: `day`, `week`, `month`,\n`quarter` or `year`. Left out, the whole range is one period, and\nthere is no `timeseries`.\n"},"lookback_window":{"anyOf":[{"type":"null"},{"maxLength":16,"type":"string"}],"description":"How far back a rate looks from the end of each period: `period`,\n`rolling_interval`, or a length such as `30d`, `1M` or `1y`. Unless\nsaid, the window of the first measure in `measure`.\n"},"measure":{"anyOf":[{"type":"null"},{"anyOf":[{"enum":["starting_mrr","new_mrr","expansion_mrr","reactivation_mrr","churned_mrr","contraction_mrr","exchange_rate_mrr","mrr_movement","run_rate_movers","mrr","mrr_growth","starting_arr","new_arr","expansion_arr","reactivation_arr","churned_arr","contraction_arr","exchange_rate_arr","arr_movement","arr","arr_growth","starting_mrr_lost","starting_mrr_gained","starting_mrr_retained","starting_arr_lost","starting_arr_gained","starting_arr_retained","gross_revenue_churn","net_revenue_churn","gross_revenue_retention","net_revenue_retention","arpa_per_month","arpa_per_year","starting_customers","net_new_customers","customers","starting_paying_customers","new_paying_customers","reactivated_paying_customers","churned_paying_customers","net_new_paying_customers","paying_customers","starting_paying_customers_lost","starting_paying_customers_retained","customer_churn","monthly_customer_churn","customer_retention","new_leads","new_signups","signups_converted_to_paid","signup_to_paid_conversion","leads_converted_to_paid","lead_to_paid_conversion","median_lead_to_paid_time","median_signup_to_paid_time","customer_lifespan","ltv"],"type":"string"},{"items":{"enum":["starting_mrr","new_mrr","expansion_mrr","reactivation_mrr","churned_mrr","contraction_mrr","exchange_rate_mrr","mrr_movement","run_rate_movers","mrr","mrr_growth","starting_arr","new_arr","expansion_arr","reactivation_arr","churned_arr","contraction_arr","exchange_rate_arr","arr_movement","arr","arr_growth","starting_mrr_lost","starting_mrr_gained","starting_mrr_retained","starting_arr_lost","starting_arr_gained","starting_arr_retained","gross_revenue_churn","net_revenue_churn","gross_revenue_retention","net_revenue_retention","arpa_per_month","arpa_per_year","starting_customers","net_new_customers","customers","starting_paying_customers","new_paying_customers","reactivated_paying_customers","churned_paying_customers","net_new_paying_customers","paying_customers","starting_paying_customers_lost","starting_paying_customers_retained","customer_churn","monthly_customer_churn","customer_retention","new_leads","new_signups","signups_converted_to_paid","signup_to_paid_conversion","leads_converted_to_paid","lead_to_paid_conversion","median_lead_to_paid_time","median_signup_to_paid_time","customer_lifespan","ltv"],"type":"string"},"minItems":1,"type":"array"}]}],"description":"The `measure` of the query the value came from. The value's own\nmeasure unless said.\n"},"revenue_types":{"anyOf":[{"type":"null"},{"items":{"enum":["recurring","usage"],"type":"string"},"type":"array"}],"description":"The kinds of revenue to count: `recurring`, `usage`, or both, which\nis what MRR, the Monthly Run Rate, counts and the default.\n"},"snapshots":{"anyOf":[{"type":"null"},{"items":{"type":"string"},"type":"array"}],"description":"Up to eight more dates to read the measures at, each at the end of\nthat day: `now-1y`, `2026-01-01`.\n"},"timezone":{"anyOf":[{"type":"null"},{"type":"string"}],"description":"The timezone to cut days and periods in, by IANA name:\n`Europe/Copenhagen`. The project's reporting timezone unless said.\n"},"to":{"anyOf":[{"type":"null"},{"type":"string"}],"description":"The end of the range, in the same forms as `from`. It includes the\nwhole day it names. Today unless said.\n"}},"type":"object"},"CreatePriceResult":{"additionalProperties":false,"description":"A price is what a subscription item is on, in the source it lives in: a\nStripe price, or a plan in a billing system that has no prices. Every\nprice is on one plan, which groups the prices of one thing you sell —\nmonthly and annual, one per currency, or the same plan in two billing\nsystems. A price is created before any subscription item names it.\n","example":{"archived":false,"created_at":"2025-09-08T14:12:30Z","currency_code":"USD","id":"0198f1a4-2e5f-7b80-a1c2-3d4e5f6a7b8c","identities":["stripe:price_1PqR8sT2uV4wX6yZ"],"interval":"month","interval_count":1,"name":"Growth monthly","object":"price","owner_source_id":"0198f1a3-0b7c-7e25-9f41-6a2d8c3e5b19","plan_id":"0198f1a4-1d2e-7a40-9b31-5c6d7e8f9a0b","pricing_model":"fixed","properties":{"tier":"mid"},"properties_by_source":{"backend":{"tier":"mid"}},"tax_behavior":"exclusive","unit_amount":"50.00"},"properties":{"archived":{"description":"Whether the price has stopped being offered. Existing subscriptions keep\nit — what somebody paid for last year does not stop being true when you\nwithdraw the price.\n","example":false,"type":"boolean"},"created_at":{"description":"When the price was first seen.","example":"2025-09-08T14:12:30Z","format":"date-time","type":"string"},"currency_code":{"anyOf":[{"type":"null"},{"maxLength":3,"minLength":3,"type":"string"}],"description":"The currency it is listed in, by its ISO 4217 code.","example":"USD"},"id":{"description":"The price's Summerbase id. Anywhere a price is addressed, one of its `identities` works too.","example":"0198f1a4-2e5f-7b80-a1c2-3d4e5f6a7b8c","format":"uuid","type":"string"},"identities":{"anyOf":[{"type":"null"},{"items":{"type":"string"},"type":"array"}],"description":"What each source calls this price, as `source:id`. Any entry addresses it — `stripe:cus_123`.","example":["stripe:price_1PqR8sT2uV4wX6yZ"]},"interval":{"anyOf":[{"type":"null"},{"enum":["day","week","month","year"],"type":"string"}],"description":"The unit it bills in. None for a price charged once.","example":"month"},"interval_count":{"anyOf":[{"type":"null"},{"minimum":1,"type":"integer"}],"description":"How many intervals between charges.","example":1},"name":{"anyOf":[{"type":"null"},{"maxLength":255,"minLength":1,"type":"string"}],"description":"What the price is called in the source it lives in, when it has a name\nof its own there — a Stripe price's nickname. Most have none, and are\ntold apart by their terms.\n","example":"Growth monthly"},"object":{"description":"The object's type, always `price`.","enum":["price"],"example":"price","type":"string"},"owner_source":{"anyOf":[{"type":"null"},{"allOf":[{"$ref":"#/components/schemas/Integration"}]}],"description":"The source the price came from. Expandable.","example":{"connected_at":"2025-09-08T14:10:05Z","created_at":"2025-09-08T14:09:47Z","current_import_id":"019a0c0f-6b1d-7e3a-8c2f-4d5e6f7a8b9c","id":"0198f1a3-0b7c-7e25-9f41-6a2d8c3e5b19","imported_at":"2025-09-08T14:26:51Z","is_source":true,"name":"Stripe","object":"integration","priority":2,"project_id":"0198f1a2-6c52-7b18-8e3f-2a9c4d6b1e07","provider_account_id":"acct_1QfR3sT8uVw2XyZa","provider_code":"stripe","sandbox":false,"slug":"stripe","webhook_installed_at":"2025-09-08T14:10:06Z","webhook_url":"https://api.summerbase.com/webhooks/integrations/0198f1a3-0b7c-7e25-9f41-6a2d8c3e5b19?c=us1"}},"owner_source_id":{"description":"The source the price came from.","example":"0198f1a3-0b7c-7e25-9f41-6a2d8c3e5b19","format":"uuid","type":"string"},"plan":{"anyOf":[{"type":"null"},{"allOf":[{"$ref":"#/components/schemas/Plan"}]}],"description":"The plan the price is on. Expandable.","example":{"archived":false,"created_at":"2025-09-08T14:12:30Z","id":"0198f1a4-1d2e-7a40-9b31-5c6d7e8f9a0b","identities":["stripe:prod_QhR2sT4uV6wX8y"],"name":"Growth","object":"plan","price_count":2,"properties":{"tier":"mid"},"properties_by_source":{"backend":{"tier":"mid"}}}},"plan_id":{"description":"The plan the price is on.","example":"0198f1a4-1d2e-7a40-9b31-5c6d7e8f9a0b","format":"uuid","type":"string"},"pricing_model":{"anyOf":[{"type":"null"},{"enum":["fixed","tiered","metered"],"type":"string"}],"description":"How an item's unit amount is arrived at on this price.","example":"fixed"},"properties":{"anyOf":[{"type":"null"},{"additionalProperties":{"anyOf":[{"type":"string"},{"type":"number"},{"type":"boolean"}]},"type":"object"}],"description":"**Your** source's properties on this price, as they stand now. What you write here is what you read back — another source's values never appear in it and can never be overwritten through it. `properties_by_source` has everyone's. When you have written a key through several of this price's ids, its value is the most recent change any of them made.","example":{"tier":"mid"}},"properties_by_source":{"anyOf":[{"type":"null"},{"additionalProperties":{"additionalProperties":{"anyOf":[{"type":"string"},{"type":"number"},{"type":"boolean"}]},"description":"Property keys and their current values.","type":"object"},"type":"object"}],"description":"Every source's properties on this price, keyed by source slug — including your own. Read-only: a write only ever reaches the source it is attributed to.","example":{"backend":{"tier":"mid"}}},"response_metadata":{"anyOf":[{"type":"null"},{"additionalProperties":false,"properties":{"warnings":{"description":"Parts of this write that were skipped — a property whose value did not\nfit its type — and things it did that were worth saying, like merging\ntwo customers. The rest of the write landed. Absent when there is\nnothing to report.\n\nSend `on_warning: \"error\"` to have these refuse the write instead,\nand come back as `error.errors` with the same codes.\n","items":{"$ref":"#/components/schemas/Warning"},"type":"array"}},"type":"object"}],"description":"About this request rather than about the record. Absent when there is nothing to say."},"tax_behavior":{"description":"Whether the amount has tax in it: `inclusive`, `exclusive`, or\n`unspecified` when the price leaves it to the source's tax settings. A\nsubscription item is worth its amount without any tax in it, at the\nrate its customer's invoices are taxed at, whatever this says.\n","enum":["inclusive","exclusive","unspecified"],"example":"exclusive","type":"string"},"unit_amount":{"anyOf":[{"type":"null"},{"format":"decimal","type":"string"}],"description":"What one unit is listed at per interval: for a `metered` price, one unit\nof usage. None for a `tiered` one, whose units cost what their tier says.\nBelow zero for a credit, which Stripe, for one, lists as a one-time\nprice with a negative amount. Includes tax when `tax_behavior` says so.\n","example":"50.00"}},"required":["archived","created_at","id","object","owner_source_id","plan_id","tax_behavior"],"type":"object"},"MetricQueryBreakdown":{"additionalProperties":false,"properties":{"categories":{"items":{"$ref":"#/components/schemas/MetricQueryNode"},"type":"array"},"key":{"description":"The dimension, as the request named it.","type":"string"}},"required":["key","categories"],"type":"object"},"DuplicateCustomers":{"additionalProperties":false,"description":"A group of duplicate customers is customers that match by the pair of fields\nyou searched by, and so may be one customer known more than once. It is a\nsuggestion: look it over, and merge what is one with `POST\n/v1/customers/{id}/merge`.\n","example":{"customer_count":2,"customer_id":"0199a7c2-4e1b-7f38-b9d2-5a6c8e0f1d73","customer_ids":["0199a7c2-4e1b-7f38-b9d2-5a6c8e0f1d73","019a3f10-8b2c-7d4e-9f6a-1b2c3d4e5f60"],"matches":[{"customer_ids":["0199a7c2-4e1b-7f38-b9d2-5a6c8e0f1d73","019a3f10-8b2c-7d4e-9f6a-1b2c3d4e5f60"],"value":"cus_QX7bN2kLp4Rt9v"}],"object":"duplicate_customers"},"properties":{"customer_count":{"description":"How many customers the group has, `customer_ids` and any beyond them.","example":2,"type":"integer"},"customer_id":{"description":"The group's oldest customer, first in `customer_ids`: the one to merge\nthe others into unless you have a reason to keep another.\n","example":"0199a7c2-4e1b-7f38-b9d2-5a6c8e0f1d73","format":"uuid","type":"string"},"customer_ids":{"description":"The group's ten oldest customers, oldest first by when they were first\nseen. Merge them, and the rest of a larger group match the one kept and\ncome back on the next search.\n","example":["0199a7c2-4e1b-7f38-b9d2-5a6c8e0f1d73","019a3f10-8b2c-7d4e-9f6a-1b2c3d4e5f60"],"items":{"format":"uuid","type":"string"},"type":"array"},"customers":{"description":"The customers of `customer_ids`, as records, in the same order.","example":[{"arr":"15000.00","churned_at":null,"country_code":"US","created_at":"2025-11-03T16:20:04Z","email":"billing@northwind.example.com","first_paying_at":"2025-12-01T10:04:00Z","first_seen_at":"2025-11-03T16:20:00Z","first_subscribed_at":"2025-11-17T10:04:00Z","id":"0199a7c2-4e1b-7f38-b9d2-5a6c8e0f1d73","identities":["stripe:cus_QX7bN2kLp4Rt9v","backend:acct_4821"],"mrr":"1250.00","name":"Northwind Labs","object":"customer","properties":{"seats":25},"properties_by_source":{"backend":{"seats":25},"stripe":{"email":"billing@northwind.example.com","name":"Northwind Labs"}},"signed_up_at":"2025-11-03T16:20:00Z","status":"paying"}],"items":{"$ref":"#/components/schemas/Customer"},"type":"array"},"matches":{"description":"Each value that links customers of `customer_ids`, and which ones hold it.","example":[{"customer_ids":["0199a7c2-4e1b-7f38-b9d2-5a6c8e0f1d73","019a3f10-8b2c-7d4e-9f6a-1b2c3d4e5f60"],"value":"cus_QX7bN2kLp4Rt9v"}],"items":{"additionalProperties":false,"properties":{"customer_ids":{"description":"The customers of `customer_ids` holding it, oldest first.","items":{"format":"uuid","type":"string"},"type":"array"},"value":{"description":"The value they share, as one of them holds it.","type":"string"}},"required":["customer_ids","value"],"type":"object"},"type":"array"},"object":{"description":"The object's type, always `duplicate_customers`.","enum":["duplicate_customers"],"example":"duplicate_customers","type":"string"}},"required":["customer_count","customer_id","customer_ids","customers","matches","object"],"type":"object"},"Organization":{"additionalProperties":false,"description":"An organization is a company using Summerbase. It holds the company's\nprojects, and the people who work in them, each through a membership.\n","example":{"cell_code":"us1","created_at":"2025-09-08T14:02:37Z","id":"0198f1a2-6c3d-7e41-9a2b-5c8d1e4f7a90","name":"Acme Analytics","object":"organization","slug":"acme-analytics"},"properties":{"cell_code":{"description":"Where the organization's data is kept, chosen from its region when it was\ncreated and never changed.\n","example":"us1","type":"string"},"created_at":{"description":"When the organization was created.","example":"2025-09-08T14:02:37Z","format":"date-time","type":"string"},"id":{"description":"The organization's unique identifier.","example":"0198f1a2-6c3d-7e41-9a2b-5c8d1e4f7a90","format":"uuid","type":"string"},"name":{"description":"The company's name, as it appears throughout Summerbase.","example":"Acme Analytics","maxLength":255,"minLength":1,"type":"string"},"object":{"description":"The object's type, always `organization`.","enum":["organization"],"example":"organization","type":"string"},"slug":{"description":"How the organization is addressed in a readable URL — the `acme` in\n`app.summerbase.com/organizations/acme`, and in its projects'\n`app.summerbase.com/projects/production@acme`. Lowercase letters, digits,\nhyphens and underscores, and never shaped like an id.\n\nUnique across every Summerbase organization, and derived from the name\nwhen you do not choose one. Changing it is allowed and breaks every\naddress that used the old one.\n","example":"acme-analytics","maxLength":100,"minLength":1,"type":"string"}},"required":["cell_code","created_at","id","name","object","slug"],"type":"object"},"Membership":{"additionalProperties":false,"description":"A membership is one person's place in an organization, with the role that\ndecides what they may do there.\n","example":{"created_at":"2025-09-08T14:02:37Z","id":"0198f1a2-6c40-7a9e-8d1b-3f5e7c9a2b64","object":"membership","organization_id":"0198f1a2-6c3d-7e41-9a2b-5c8d1e4f7a90","role":"admin","user_id":"0198f1a2-6b10-7d2a-b4c5-9e8f7a6b5c41"},"properties":{"created_at":{"description":"When the user joined the organization.","example":"2025-09-08T14:02:37Z","format":"date-time","type":"string"},"id":{"description":"The membership's unique identifier.","example":"0198f1a2-6c40-7a9e-8d1b-3f5e7c9a2b64","format":"uuid","type":"string"},"object":{"description":"The object's type, always `membership`.","enum":["membership"],"example":"membership","type":"string"},"organization":{"anyOf":[{"type":"null"},{"allOf":[{"$ref":"#/components/schemas/Organization"}]}],"description":"The organization the membership is in. Expandable.","example":{"cell_code":"us1","created_at":"2025-09-08T14:02:37Z","id":"0198f1a2-6c3d-7e41-9a2b-5c8d1e4f7a90","name":"Acme Analytics","object":"organization","slug":"acme-analytics"}},"organization_id":{"description":"The organization the membership is in.","example":"0198f1a2-6c3d-7e41-9a2b-5c8d1e4f7a90","format":"uuid","type":"string"},"role":{"description":"What the user may do in the organization, and in every project under it.\n","enum":["guest","viewer","writer","admin","owner"],"example":"admin","type":"string"},"user":{"anyOf":[{"type":"null"},{"allOf":[{"$ref":"#/components/schemas/User"}]}],"description":"Whose membership it is. Expandable.","example":{"created_at":"2025-09-08T13:58:02Z","email":"maya@example.com","id":"0198f1a2-6b10-7d2a-b4c5-9e8f7a6b5c41","name":"Maya Chen","oauth_provider":"google","object":"user"}},"user_id":{"description":"Whose membership it is.","example":"0198f1a2-6b10-7d2a-b4c5-9e8f7a6b5c41","format":"uuid","type":"string"}},"required":["created_at","id","object","organization_id","role","user_id"],"type":"object"},"UpdateSubscriptionResult":{"additionalProperties":false,"description":"A subscription is what a customer is subscribed to, in the one source it\nlives in. It is made of items that carry the prices, and its\n`monthly_amount` is what they are worth a month; a customer's `mrr` is the\nsum over their subscriptions.\n","example":{"annual_amount":"15000.00","billing_amounts":[{"interval":"month","interval_count":1,"net_amount":"1250.00"}],"cancels_at":null,"currency_code":"USD","current_period_ends_at":"2026-11-01T10:04:00Z","customer_id":"0199a7c2-4e1b-7f38-b9d2-5a6c8e0f1d73","ended_at":null,"id":"0199a7c2-5b3e-7a14-8c6d-2f9e4b7a1c05","identities":["stripe:sub_1QhT5uV7wX9yZ2aB"],"monthly_amount":"1250.00","object":"subscription","owner_source_id":"0198f1a3-0b7c-7e25-9f41-6a2d8c3e5b19","properties":{"account_manager":"Priya Shah"},"properties_by_source":{"backend":{"account_manager":"Priya Shah"},"stripe":{"contract_id":"C-2025-118"}},"recorded_at":"2025-11-17T10:04:03Z","started_at":"2025-11-17T10:04:00Z","status":"active","trial_ends_at":"2025-12-01T10:04:00Z"},"properties":{"annual_amount":{"anyOf":[{"type":"null"},{"format":"decimal","type":"string"}],"description":"What this subscription is worth a year, in `currency_code`:\n`monthly_amount` times twelve.\n","example":"15000.00"},"billing_amounts":{"anyOf":[{"type":"null"},{"items":{"$ref":"#/components/schemas/SubscriptionBillingAmount"},"type":"array"}],"description":"What this subscription comes to each time it bills: the items still on\nit, their `net_amount` summed per billing interval, longest interval\nfirst. One entry for nearly every subscription; one per interval for a\nsubscription whose items bill on different ones, such as a yearly fee\nbeside a monthly one. Its price whatever its status — a trial still has\none. `monthly_amount` is what it is worth on one footing.\n","example":[{"interval":"month","interval_count":1,"net_amount":"1250.00"}]},"cancels_at":{"anyOf":[{"type":"null"},{"format":"date-time","type":"string"}],"description":"A cancellation already booked. It is still in service until then, and\nthe source says so with `status: canceled` when it stops.\n"},"currency":{"anyOf":[{"type":"null"},{"allOf":[{"$ref":"#/components/schemas/Currency"}]}],"description":"The currency it bills in. Expandable.","example":{"code":"USD","name":"US dollar","object":"currency"}},"currency_code":{"description":"The currency it bills in, by its ISO 4217 code.","example":"USD","maxLength":3,"minLength":3,"type":"string"},"current_period_ends_at":{"anyOf":[{"type":"null"},{"format":"date-time","type":"string"}],"description":"When the current billing period ends. The next charge is due then,\nunless a cancellation is booked for it.\n","example":"2026-11-01T10:04:00Z"},"customer":{"anyOf":[{"type":"null"},{"allOf":[{"$ref":"#/components/schemas/Customer"}]}],"description":"The customer who is subscribed. Expandable.","example":{"arr":"15000.00","country_code":"US","created_at":"2025-11-03T16:20:04Z","email":"billing@northwind.example.com","first_paying_at":"2025-12-01T10:04:00Z","first_seen_at":"2025-11-03T16:20:00Z","first_subscribed_at":"2025-11-17T10:04:00Z","id":"0199a7c2-4e1b-7f38-b9d2-5a6c8e0f1d73","identities":["stripe:cus_QX7bN2kLp4Rt9v","backend:acct_4821"],"mrr":"1250.00","name":"Northwind Labs","object":"customer","properties":{"seats":25},"properties_by_source":{"backend":{"seats":25},"stripe":{"email":"billing@northwind.example.com","name":"Northwind Labs"}},"signed_up_at":"2025-11-03T16:20:00Z","status":"paying"}},"customer_id":{"description":"The customer who is subscribed.","example":"0199a7c2-4e1b-7f38-b9d2-5a6c8e0f1d73","format":"uuid","type":"string"},"ended_at":{"anyOf":[{"type":"null"},{"format":"date-time","type":"string"}],"description":"When service stopped, beside `status: canceled`. Clear it, with `status`\nback to `active`, to resume a subscription; the same id may be brought\nback as often as the source does so.\n"},"id":{"description":"The subscription's Summerbase id. Anywhere a subscription is addressed, one of its `identities` works too.","example":"0199a7c2-5b3e-7a14-8c6d-2f9e4b7a1c05","format":"uuid","type":"string"},"identities":{"anyOf":[{"type":"null"},{"items":{"type":"string"},"type":"array"}],"description":"What each source calls this subscription, as `source:id`. Any entry addresses it — `stripe:cus_123`.","example":["stripe:sub_1QhT5uV7wX9yZ2aB"]},"items":{"anyOf":[{"type":"null"},{"items":{"$ref":"#/components/schemas/SubscriptionItem"},"type":"array"}],"description":"The items it is made of, and where the money is. Expandable.","example":[{"created_at":"2025-11-17T10:04:03Z","deleted":false,"discount_amount":"0","id":"0199a7c2-5b41-7c62-9d3e-6a1f8b2c4d97","identities":["stripe:si_QhT6vW8xY0zA3b"],"interval":"month","interval_count":1,"net_amount":"1250.00","object":"subscription_item","plan_id":"0198f1a4-1d2e-7a40-9b31-5c6d7e8f9a0b","plan_name":"Growth","price_id":"0198f1a4-2e5f-7b80-a1c2-3d4e5f6a7b8c","price_name":"Growth monthly","pricing_model":"fixed","properties":{"seat_type":"standard"},"properties_by_source":{"backend":{"seat_type":"standard"}},"quantity":"25","subscription_id":"0199a7c2-5b3e-7a14-8c6d-2f9e4b7a1c05","unit_amount":"50.00"}]},"monthly_amount":{"description":"What this subscription is worth a month, in `currency_code`, after the\nitems' discounts — **derived, not reported.** The sum of what its items\nare worth; zero while it is on trial, paused or ended, and zero on a\nfree plan. A customer's `mrr` is these summed across their subscriptions, in\nthe reporting currency.\n","example":"1250.00","format":"decimal","type":"string"},"object":{"description":"The object's type, always `subscription`.","enum":["subscription"],"example":"subscription","type":"string"},"owner_source":{"anyOf":[{"type":"null"},{"allOf":[{"$ref":"#/components/schemas/Integration"}]}],"description":"The source this subscription lives in — the only one that may write its\nown fields. Any source may attach properties and identities to it.\n Expandable.","example":{"connected_at":"2025-09-08T14:10:05Z","created_at":"2025-09-08T14:09:47Z","current_import_id":"019a0c0f-6b1d-7e3a-8c2f-4d5e6f7a8b9c","id":"0198f1a3-0b7c-7e25-9f41-6a2d8c3e5b19","imported_at":"2025-09-08T14:26:51Z","is_source":true,"name":"Stripe","object":"integration","priority":2,"project_id":"0198f1a2-6c52-7b18-8e3f-2a9c4d6b1e07","provider_account_id":"acct_1QfR3sT8uVw2XyZa","provider_code":"stripe","sandbox":false,"slug":"stripe","webhook_installed_at":"2025-09-08T14:10:06Z","webhook_url":"https://api.summerbase.com/webhooks/integrations/0198f1a3-0b7c-7e25-9f41-6a2d8c3e5b19?c=us1"}},"owner_source_id":{"description":"The source this subscription lives in — the only one that may write its\nown fields. Any source may attach properties and identities to it.\n","example":"0198f1a3-0b7c-7e25-9f41-6a2d8c3e5b19","format":"uuid","type":"string"},"properties":{"anyOf":[{"type":"null"},{"additionalProperties":{"anyOf":[{"type":"string"},{"type":"number"},{"type":"boolean"}]},"type":"object"}],"description":"**Your** source's properties on this subscription, as they stand now. What you write here is what you read back — another source's values never appear in it and can never be overwritten through it. `properties_by_source` has everyone's. When you have written a key through several of this subscription's ids, its value is the most recent change any of them made.","example":{"account_manager":"Priya Shah"}},"properties_by_source":{"anyOf":[{"type":"null"},{"additionalProperties":{"additionalProperties":{"anyOf":[{"type":"string"},{"type":"number"},{"type":"boolean"}]},"description":"Property keys and their current values.","type":"object"},"type":"object"}],"description":"Every source's properties on this subscription, keyed by source slug — including your own. Read-only: a write only ever reaches the source it is attributed to.","example":{"backend":{"account_manager":"Priya Shah"},"stripe":{"contract_id":"C-2025-118"}}},"recorded_at":{"description":"When Summerbase first recorded it. Never set by a caller.","example":"2025-11-17T10:04:03Z","format":"date-time","type":"string"},"response_metadata":{"anyOf":[{"type":"null"},{"additionalProperties":false,"properties":{"warnings":{"description":"Parts of this write that were skipped — a property whose value did not\nfit its type — and things it did that were worth saying, like merging\ntwo customers. The rest of the write landed. Absent when there is\nnothing to report.\n\nSend `on_warning: \"error\"` to have these refuse the write instead,\nand come back as `error.errors` with the same codes.\n","items":{"$ref":"#/components/schemas/Warning"},"type":"array"}},"type":"object"}],"description":"About this request rather than about the record. Absent when there is nothing to say."},"started_at":{"description":"When the subscription began — **derived, not reported.**\n\nIt is the earliest instant anything was asserted about the subscription or\nits items. A source cannot say it began earlier than it described it: send\nthe earliest write with `effective_at` at the real start instead, which is\nwhat an import of history does anyway.\n\nIt moves earlier when a later write describes an earlier moment.\n","example":"2025-11-17T10:04:00Z","format":"date-time","type":"string"},"status":{"description":"Where it is in its life. Not whether it has been paid for.","enum":["pending","trialing","active","paused","canceled"],"example":"active","type":"string"},"trial_ends_at":{"anyOf":[{"type":"null"},{"format":"date-time","type":"string"}],"description":"When the trial ends, or ended. A subscription that trials starts\ntrialing, so when it began is `started_at` and its status history.\n","example":"2025-12-01T10:04:00Z"}},"required":["currency_code","customer_id","id","monthly_amount","object","owner_source_id","recorded_at","started_at","status"],"type":"object"},"Project":{"additionalProperties":false,"description":"A project holds one product's revenue data — its integrations, customers,\nsubscriptions and plans — and the settings its metrics are computed with. An\nAPI key belongs to one project, and works in it alone.\n","example":{"checklist_dismissed_at":null,"churn_timing":"at_expiration","created_at":"2025-09-08T14:02:37Z","id":"0198f1a2-6c52-7b18-8e3f-2a9c4d6b1e07","movement_collapse_window_days":7,"name":"Production","object":"project","organization_id":"0198f1a2-6c3d-7e41-9a2b-5c8d1e4f7a90","recompute_failed_at":null,"recompute_remaining":0,"recompute_started_at":null,"recompute_total":0,"reporting_cadence":"monthly","reporting_currency_code":"USD","reporting_timezone":"America/New_York","slug":"production"},"properties":{"checklist_dismissed_at":{"anyOf":[{"type":"null"},{"format":"date-time","type":"string"}],"description":"When the project's Get started checklist was dismissed in Summerbase, or\nnull while it is offered.\n"},"churn_timing":{"description":"When a subscription's revenue stops counting: `at_expiration`, when it\nends, or `at_cancellation`, when a cancellation is booked for it.\n\nUnder `at_cancellation` a subscription with `cancels_at` set is worth\nnothing from the moment the source said so, though it is still in\nservice: the customer's MRR drops then, the movement is churn, or\ncontraction if something else is still live, and a customer with nothing\nelse live is `churned`. Withdrawing the cancellation brings the revenue\nback. A trial booked to end loses nothing, having nothing to lose. A\nStripe import dates a booking at Stripe's `canceled_at`, which holds only\nthe latest request, so an earlier or withdrawn one is not in the history.\n\nChanging it moves the metrics of every customer in the project.\n","enum":["at_expiration","at_cancellation"],"example":"at_expiration","type":"string"},"created_at":{"description":"When the project was created.","example":"2025-09-08T14:02:37Z","format":"date-time","type":"string"},"id":{"description":"The project's unique identifier.","example":"0198f1a2-6c52-7b18-8e3f-2a9c4d6b1e07","format":"uuid","type":"string"},"movement_collapse_window_days":{"description":"How close together two changes to a customer's revenue have to be to count\nas one.\n\nSigning up at $100 and upgrading to $500 the next day is $500 of new\nrevenue, not $100 of new plus $400 of expansion, and a cancellation\nreversed within the window is neither a churn nor a reactivation. The\ncollapsed movement is dated at the first change in the window, so a\ncustomer who arrives on the 30th and upgrades on the 2nd still arrived in\nthe first month. `0` records every change as it happened.\n\nAn elapsed duration, not a calendar one: seven days is 168 hours wherever\nthe project reports from. Changing it moves the metrics of every customer in\nthe project, and a closed period can move while it is still inside the\nwindow.\n","example":7,"maximum":90,"minimum":0,"type":"integer"},"name":{"description":"What the project is called.","example":"Production","maxLength":255,"minLength":1,"type":"string"},"object":{"description":"The object's type, always `project`.","enum":["project"],"example":"project","type":"string"},"organization":{"anyOf":[{"type":"null"},{"allOf":[{"$ref":"#/components/schemas/Organization"}]}],"description":"The organization that owns the project. Expandable.","example":{"cell_code":"us1","created_at":"2025-09-08T14:02:37Z","id":"0198f1a2-6c3d-7e41-9a2b-5c8d1e4f7a90","name":"Acme Analytics","object":"organization","slug":"acme-analytics"}},"organization_id":{"description":"The organization that owns the project.","example":"0198f1a2-6c3d-7e41-9a2b-5c8d1e4f7a90","format":"uuid","type":"string"},"recompute_failed_at":{"anyOf":[{"type":"null"},{"format":"date-time","type":"string"}],"description":"When the last recompute gave up, or empty when none has since the last one\nsucceeded.\n\n"},"recompute_remaining":{"description":"How many of them are still behind.\n\nCounts only the customers the recompute started with.\n","example":0,"type":"integer"},"recompute_started_at":{"anyOf":[{"type":"null"},{"format":"date-time","type":"string"}],"description":"When the recompute now running began, or empty when none is.\n\nOne runs after a setting that decides how the project is measured changes.\n"},"recompute_total":{"description":"How many customers that recompute has to get through.","example":0,"type":"integer"},"reporting_cadence":{"description":"Which cadence the project quotes its metrics at: `monthly`, MRR, or\n`annual`, ARR.\n\nEvery measure is answered at either cadence whatever this says. What it\ndecides is which of the two Summerbase shows where it shows one.\n","enum":["monthly","annual"],"example":"monthly","type":"string"},"reporting_currency":{"anyOf":[{"type":"null"},{"allOf":[{"$ref":"#/components/schemas/Currency"}]}],"description":"The currency every amount is reported in. Expandable.","example":{"code":"USD","name":"US dollar","object":"currency"}},"reporting_currency_code":{"description":"The currency every amount is reported in, by its ISO 4217 code.\nA project's first import sets it to the currency most of the imported\nsubscriptions are billed in, and nothing changes it afterwards but\nthis action.\n\nAmounts are converted at the rate for the moment being measured, so a\nproject billing in three currencies has one number to look at.\n","example":"USD","maxLength":3,"minLength":3,"type":"string"},"reporting_timezone":{"description":"The timezone every chart's buckets are cut in, by its IANA name:\n`Europe/Copenhagen`, `America/New_York`, `UTC`. An offset like `+02:00`\nand an abbreviation like `CET` are refused.\n\nIt decides which day a midnight-adjacent event falls in, and where a month\nstarts.\n","example":"America/New_York","type":"string"},"slug":{"description":"How the project is addressed in a readable URL — the `production` in\n`app.summerbase.com/projects/production@acme`, or that one slug alone\nwhen it is the organization's too. Lowercase letters, digits, hyphens and\nunderscores, and never shaped like an id.\n\nUnique within the organization: one already used there is refused.\nDerived from the name when you do not choose one. Changing it is allowed\nand breaks every address that used the old one.\n","example":"production","maxLength":100,"minLength":1,"type":"string"}},"required":["churn_timing","created_at","id","movement_collapse_window_days","name","object","organization_id","recompute_remaining","recompute_total","reporting_cadence","reporting_currency_code","reporting_timezone","slug"],"type":"object"},"FilterCondition":{"additionalProperties":false,"description":"A condition over what is known about a customer. Either a group — `all` or\n`any` over a list of conditions, or `not` over one — a box —\n`has_subscription` or `has_subscription_item` over one condition about one\nsuch record — or a comparison: a `key` beside exactly one operator. At most\nfour levels deep, boxes not counted, twenty-five comparisons, and five boxes,\na bare comparison about a subscription or an item counting as one.\n\nA key is fully qualified: `customer.country_code`,\n`subscription.status`, `customer.properties.hubspot.industry`. See\n[Filters](/docs/api/filters) for how to write a filter, with examples.\n","properties":{"all":{"description":"Matches when every condition in the list does. At least one.","items":{"$ref":"#/components/schemas/FilterCondition"},"type":"array"},"any":{"description":"Matches when at least one condition in the list does. At least one.","items":{"$ref":"#/components/schemas/FilterCondition"},"type":"array"},"at":{"anyOf":[{"enum":["window","now"],"type":"string"},{"type":"string"}],"description":"When the value is read: `window`, as things stood at the time over the window being measured — a level by the value in force at its end, a movement by the value when the money moved, a cohort by the value at its start; `now`; an instant relative to it, `now-3M` or `now-1M/M`; or a datetime as an ISO 8601 date or date and time, UTC when it names no zone, so a list can ask who was active on a given day. Left out, it is what the query reads at: `now` on a list, `window` on a query over periods. A list has no period, and refuses `window`."},"contains":{"anyOf":[{"type":"string"},{"type":"boolean"},{"type":"number"}],"description":"The value contains this, ignoring case. For a key that holds a string."},"equals":{"anyOf":[{"type":"string"},{"type":"boolean"},{"type":"number"}],"description":"The value is this, read as what the key holds: a string ignoring case, a number by value, a boolean, or a datetime."},"greater_than":{"anyOf":[{"type":"number"},{"type":"string"}],"description":"The value is above this. For a key that holds a number — sent as a JSON number or a decimal string — or a datetime, as an ISO 8601 string or relative to now, `now-3M`."},"greater_than_or_equals":{"anyOf":[{"type":"number"},{"type":"string"}],"description":"The value is this or above it, taking what `greater_than` takes."},"has_subscription":{"allOf":[{"$ref":"#/components/schemas/FilterCondition"}],"description":"Matches when the customer has a subscription that matches the condition. Everything inside is about that one subscription."},"has_subscription_item":{"allOf":[{"$ref":"#/components/schemas/FilterCondition"}],"description":"Matches when there is an item that matches the condition — of the subscription the box is in, or of any subscription at the top level. Everything inside is about that one item."},"includes":{"anyOf":[{"type":"string"},{"type":"boolean"},{"type":"number"}],"description":"One of the values is this. For a key that holds a list, such as `customer.external_ids.stripe`, and no other."},"key":{"description":"What is compared — `customer.status`, `subscription_item.quantity`, `customer.properties.hubspot.industry`.","type":"string"},"less_than":{"anyOf":[{"type":"number"},{"type":"string"}],"description":"The value is below this. For a key that holds a number — sent as a JSON number or a decimal string — or a datetime, as an ISO 8601 string or relative to now, `now-3M`."},"less_than_or_equals":{"anyOf":[{"type":"number"},{"type":"string"}],"description":"The value is this or below it, taking what `less_than` takes."},"not":{"allOf":[{"$ref":"#/components/schemas/FilterCondition"}],"description":"Matches when the condition does not."},"not_equals":{"anyOf":[{"type":"string"},{"type":"boolean"},{"type":"number"}],"description":"The value is not this, read as `equals` reads it — which a customer with no value at all also satisfies."},"present":{"description":"Whether there is any value at all.","type":"boolean"}},"type":"object"},"UpdatePlanResult":{"additionalProperties":false,"description":"A plan is one thing you sell — Pro, Team — and the prices it is billed as:\nmonthly and annual, one per currency, and the prices of the same plan in\nevery billing system you have used. Metrics can be broken down and filtered\nby plan. A plan is created with the first price that names it, or ahead of\ntime, and plans can be renamed and merged without touching the\nsubscriptions on them.\n","example":{"archived":false,"created_at":"2025-09-08T14:12:30Z","id":"0198f1a4-1d2e-7a40-9b31-5c6d7e8f9a0b","identities":["stripe:prod_QhR2sT4uV6wX8y"],"name":"Growth","object":"plan","price_count":2,"properties":{"tier":"mid"},"properties_by_source":{"backend":{"tier":"mid"}}},"properties":{"archived":{"description":"Whether the plan has stopped being offered — the `archived` property,\nresolved to the highest-priority source that says one; Stripe says it\nof a product it has archived. Write `properties.archived` to change it.\nExisting subscriptions keep the plan: what somebody paid for last year\ndoes not stop being true when you withdraw it.\n","example":false,"type":"boolean"},"created_at":{"description":"When the plan was first seen.","example":"2025-09-08T14:12:30Z","format":"date-time","type":"string"},"id":{"description":"The plan's Summerbase id. Anywhere a plan is addressed, one of its `identities` works too.","example":"0198f1a4-1d2e-7a40-9b31-5c6d7e8f9a0b","format":"uuid","type":"string"},"identities":{"anyOf":[{"type":"null"},{"items":{"type":"string"},"type":"array"}],"description":"What each source calls this plan, as `source:id`. Any entry addresses it — `stripe:cus_123`.","example":["stripe:prod_QhR2sT4uV6wX8y"]},"name":{"anyOf":[{"type":"null"},{"maxLength":255,"type":"string"}],"description":"What the plan is called in a filter, a legend or a breakdown — the\n`name` property, resolved to the highest-priority source that has one.\nWrite `properties.name` to change it. Null when no source names it.\n","example":"Growth"},"object":{"description":"The object's type, always `plan`.","enum":["plan"],"example":"plan","type":"string"},"price_count":{"description":"How many prices are on this plan.","example":2,"type":"integer"},"prices":{"anyOf":[{"type":"null"},{"items":{"$ref":"#/components/schemas/Price"},"type":"array"}],"description":"The prices on this plan, in every source. Expandable.","example":[{"archived":false,"created_at":"2025-09-08T14:12:30Z","currency_code":"USD","id":"0198f1a4-2e5f-7b80-a1c2-3d4e5f6a7b8c","identities":["stripe:price_1PqR8sT2uV4wX6yZ"],"interval":"month","interval_count":1,"name":"Growth monthly","object":"price","owner_source_id":"0198f1a3-0b7c-7e25-9f41-6a2d8c3e5b19","plan_id":"0198f1a4-1d2e-7a40-9b31-5c6d7e8f9a0b","pricing_model":"fixed","properties":{"tier":"mid"},"properties_by_source":{"backend":{"tier":"mid"}},"tax_behavior":"exclusive","unit_amount":"50.00"}]},"properties":{"anyOf":[{"type":"null"},{"additionalProperties":{"anyOf":[{"type":"string"},{"type":"number"},{"type":"boolean"}]},"type":"object"}],"description":"**Your** source's properties on this plan, as they stand now. What you write here is what you read back — another source's values never appear in it and can never be overwritten through it. `properties_by_source` has everyone's. When you have written a key through several of this plan's ids, its value is the most recent change any of them made.","example":{"tier":"mid"}},"properties_by_source":{"anyOf":[{"type":"null"},{"additionalProperties":{"additionalProperties":{"anyOf":[{"type":"string"},{"type":"number"},{"type":"boolean"}]},"description":"Property keys and their current values.","type":"object"},"type":"object"}],"description":"Every source's properties on this plan, keyed by source slug — including your own. Read-only: a write only ever reaches the source it is attributed to.","example":{"backend":{"tier":"mid"}}},"response_metadata":{"anyOf":[{"type":"null"},{"additionalProperties":false,"properties":{"warnings":{"description":"Parts of this write that were skipped — a property whose value did not\nfit its type — and things it did that were worth saying, like merging\ntwo customers. The rest of the write landed. Absent when there is\nnothing to report.\n\nSend `on_warning: \"error\"` to have these refuse the write instead,\nand come back as `error.errors` with the same codes.\n","items":{"$ref":"#/components/schemas/Warning"},"type":"array"}},"type":"object"}],"description":"About this request rather than about the record. Absent when there is nothing to say."}},"required":["archived","created_at","id","object","price_count"],"type":"object"},"MetricQueryResult":{"additionalProperties":false,"description":"Each measure's value in `values`, and per period in `timeseries` when there is an interval; the same at other dates in `snapshots`, and per category in `breakdown`, when asked for. `query` is the query as it was answered, every default filled in, and `meta` describes the range, the measures and the periods.","properties":{"breakdown":{"$ref":"#/components/schemas/MetricQueryBreakdown"},"meta":{"$ref":"#/components/schemas/MetricQueryMeta"},"query":{"$ref":"#/components/schemas/MetricQueryResolved"},"snapshots":{"items":{"$ref":"#/components/schemas/MetricQuerySnapshot"},"type":"array"},"timeseries":{"items":{"$ref":"#/components/schemas/MetricQueryEntry"},"type":"array"},"values":{"$ref":"#/components/schemas/MetricQueryValues"}},"required":["values","query","meta"],"type":"object"},"IntegrationLogEntry":{"additionalProperties":false,"description":"An integration log entry is one line of an integration's log: something a person\nsetting up or watching the integration should know, such as how an import\nwent or a record that could not be written.\n","example":{"id":"019a0c11-3d4e-7f5a-9b6c-7d8e9f0a1b2c","integration_id":"0198f1a3-0b7c-7e25-9f41-6a2d8c3e5b19","level":"info","message":"Subscription stripe:sub_1QhT5uV7wX9yZ2aB updated: items.","object":"source_log_entry","recorded_at":"2026-06-12T15:47:11Z","subject_ref":"stripe:sub_1QhT5uV7wX9yZ2aB","subject_type":"subscription"},"properties":{"id":{"description":"The entry's unique identifier.","example":"019a0c11-3d4e-7f5a-9b6c-7d8e9f0a1b2c","format":"uuid","type":"string"},"integration":{"anyOf":[{"type":"null"},{"allOf":[{"$ref":"#/components/schemas/Integration"}]}],"description":"The integration whose log this is. Expandable.","example":{"connected_at":"2025-09-08T14:10:05Z","created_at":"2025-09-08T14:09:47Z","current_import_id":"019a0c0f-6b1d-7e3a-8c2f-4d5e6f7a8b9c","id":"0198f1a3-0b7c-7e25-9f41-6a2d8c3e5b19","imported_at":"2025-09-08T14:26:51Z","is_source":true,"name":"Stripe","object":"integration","priority":2,"project_id":"0198f1a2-6c52-7b18-8e3f-2a9c4d6b1e07","provider_account_id":"acct_1QfR3sT8uVw2XyZa","provider_code":"stripe","sandbox":false,"slug":"stripe","webhook_installed_at":"2025-09-08T14:10:06Z","webhook_url":"https://api.summerbase.com/webhooks/integrations/0198f1a3-0b7c-7e25-9f41-6a2d8c3e5b19?c=us1"}},"integration_id":{"description":"The integration whose log this is.","example":"0198f1a3-0b7c-7e25-9f41-6a2d8c3e5b19","format":"uuid","type":"string"},"level":{"description":"How much it should worry the person reading it.","enum":["debug","info","warning","error"],"example":"info","type":"string"},"message":{"description":"What happened, in a sentence.","example":"Subscription stripe:sub_1QhT5uV7wX9yZ2aB updated: items.","maxLength":2000,"minLength":1,"type":"string"},"object":{"description":"The object's type, always `source_log_entry`.","enum":["source_log_entry"],"example":"source_log_entry","type":"string"},"recorded_at":{"description":"When it happened.","example":"2026-06-12T15:47:11Z","format":"date-time","type":"string"},"subject_ref":{"anyOf":[{"type":"null"},{"maxLength":320,"type":"string"}],"description":"The record the entry is about, as `source:id`, when it is about one.","example":"stripe:sub_1QhT5uV7wX9yZ2aB"},"subject_type":{"anyOf":[{"type":"null"},{"enum":["customer","subscription","subscription_item","plan","price"],"type":"string"}],"description":"What kind of record `subject_ref` names, when the entry is about one.","example":"subscription"}},"required":["id","integration_id","level","message","object","recorded_at"],"type":"object"},"MetricQueryResolved":{"additionalProperties":false,"description":"The query as it was answered: every input you sent, and the default for each one you left out. Send it back to `POST /v1/metrics`, changed or not, to ask again. Every key is there, empty or null when nothing was asked. `from` and `to` are as written; `meta` has the dates they came to.","properties":{"breakdown":{"description":"The dimensions, each with its measures resolved; empty when none.","items":{"additionalProperties":false,"properties":{"at":{"description":"`window`, `now`, or a datetime.","type":"string"},"boundaries":{"items":{"type":"number"},"type":"array"},"key":{"type":"string"},"max_categories":{"type":"integer"},"measure":{"items":{"type":"string"},"type":"array"},"truncate":{"type":"string"}},"required":["key","measure"],"type":"object"},"type":"array"},"currency_code":{"description":"The currency every amount is in, by ISO 4217 code.","type":"string"},"filter":{"anyOf":[{"type":"null"},{"description":"The filter in its object form, whichever form it was sent in.","type":"object"}]},"from":{"description":"The start of the range, as written.","type":"string"},"interval":{"anyOf":[{"type":"null"},{"description":"How the range is cut; null for no timeseries.","enum":["day","week","month","quarter","year"],"type":"string"}]},"lookback_window":{"type":"string"},"measure":{"description":"The measures the root and the snapshots carry.","items":{"type":"string"},"type":"array"},"revenue_types":{"items":{"enum":["recurring","usage"],"type":"string"},"type":"array"},"snapshots":{"description":"The instants, as written; empty when none.","items":{"type":"string"},"type":"array"},"timezone":{"description":"The IANA zone the range is cut into days and periods in.","type":"string"},"to":{"description":"The end of the range, as written.","type":"string"}},"required":["from","to","interval","measure","breakdown","filter","snapshots","currency_code","timezone","revenue_types","lookback_window"],"type":"object"},"User":{"additionalProperties":false,"description":"A user is a person with a Summerbase account. What they may do in an\norganization is the role on their membership of it. A request made with an\nAPI key is made as nobody, so it has no user.\n","example":{"created_at":"2025-09-08T13:58:02Z","email":"maya@example.com","id":"0198f1a2-6b10-7d2a-b4c5-9e8f7a6b5c41","name":"Maya Chen","oauth_provider":"google","object":"user"},"properties":{"created_at":{"description":"When the account was created.","example":"2025-09-08T13:58:02Z","format":"date-time","type":"string"},"email":{"description":"The email address the account signs in with.","example":"maya@example.com","maxLength":255,"type":"string"},"id":{"description":"The user's unique identifier.","example":"0198f1a2-6b10-7d2a-b4c5-9e8f7a6b5c41","format":"uuid","type":"string"},"name":{"description":"The name on the account.","example":"Maya Chen","type":"string"},"oauth_provider":{"anyOf":[{"type":"null"},{"type":"string"}],"description":"The provider the account signs in with, or null for email and password.","example":"google"},"object":{"description":"The object's type, always `user`.","enum":["user"],"example":"user","type":"string"}},"required":["created_at","email","id","name","object"],"type":"object"},"Measure":{"additionalProperties":false,"description":"A measure is a kind of number a metrics query can ask for, such as\n`mrr` or `customer_churn`: what it is called, its unit, how it is read,\nand what it can be broken down by.\n","example":{"default_lookback_window":"period","description":"`mrr` is the Monthly Run Rate: revenue per month from every active subscription at the end of each window, after discounts, recurring and usage alike.","dimensions":["customer.*","subscription.*","subscription_item.*","movement_type","revenue_type"],"drill_through_unit":"money","family":"run_rate","filter_keys":["customer.*","subscription.*","subscription_item.*"],"good_direction":"up","id":"mrr","label":"MRR","measure_type":"level","object":"measure","unit":"money","window_reading":"end"},"properties":{"default_lookback_window":{"description":"The window it is read over unless a query says otherwise, as\n`lookback_window` takes it: `period` for a level or a flow,\n`rolling_interval` for a rate, and `1y` for a retention rate, which is\nquoted over a year. `POST /v1/metrics` reads every measure over the\nfirst one's unless the query says otherwise.\n","example":"period","type":"string"},"description":{"description":"What the number is, in a sentence or two, and its formula where it has\none. It opens with its own key and names every other measure by its\nkey in backticks: \"`mrr_growth` is how much `mrr` grew or fell over each\nwindow, as a percentage. Formula: `mrr_movement` divided by\n`starting_mrr`.\" Put each measure's `label` in place of its key to show\nit to a person.\n","example":"`mrr` is the Monthly Run Rate: revenue per month from every active subscription at the end of each window, after discounts, recurring and usage alike.","type":"string"},"dimensions":{"description":"What it can be broken down by: `customer.*` for any key about the customer,\n`subscription.*` and `subscription_item.*` likewise, and the revenue\nmovements' own — `movement_type`, `revenue_type`. A field holding a\n`string_list` is never one: a record with several values would fall\nin several categories.\n","example":["customer.*","subscription.*","subscription_item.*","movement_type","revenue_type"],"items":{"type":"string"},"type":"array"},"drill_through_unit":{"anyOf":[{"type":"null"},{"enum":["money","days","count"],"type":"string"}],"description":"Whether `POST /v1/metrics/drill_through/customers` lists the customers behind a\nvalue of it, and what each one's `contribution` there is in: `money`,\ntheir share of an amount; `days`, what they took; or `count`, where each\ncustomer is one and `contribution` is null. Null for a measure with nobody\nto list. A rate lists whoever is behind what it is over.\n","example":"money"},"family":{"description":"What it is about, for listing measures under headings: `run_rate`,\n`run_rate_cohort`, `customers`, `customer_cohort`,\n`churn_and_retention` or `revenue_per_account`.\n","enum":["run_rate","run_rate_cohort","customers","customer_cohort","churn_and_retention","revenue_per_account","funnel","lifetime"],"example":"run_rate","type":"string"},"filter_keys":{"description":"What a `filter` on it may compare: `customer.*` for any key about the\ncustomer, and `subscription.*` and `subscription_item.*` likewise, as\n`GET /v1/fields` lists them.\n","example":["customer.*","subscription.*","subscription_item.*"],"items":{"type":"string"},"type":"array"},"good_direction":{"anyOf":[{"type":"null"},{"enum":["up","down"],"type":"string"}],"description":"Which way it moving is good news: `up` for revenue and retention,\n`down` for churn and what was lost. Absent for a measure that says\nnothing either way — a starting value, or how many customers moved.\n","example":"up"},"id":{"description":"The key it comes under in an answer, and the name `measure` takes.","example":"mrr","type":"string"},"label":{"description":"What to call it: `Revenue`, `Customer churn`.","example":"MRR","type":"string"},"measure_type":{"description":"What sort of value it has, which says how to draw it and what may be\nsummed: `level`, a value at an instant — categories add up to it,\nperiods do not; `flow`, what happened inside the window — categories\nand periods both add up; or `rate`, one value over another, a\npercentage or an amount per account — nothing adds up to it, so its\ncategories are never stacked or totaled.\n","enum":["level","flow","rate"],"example":"level","type":"string"},"object":{"description":"The object's type, always `measure`.","enum":["measure"],"example":"measure","type":"string"},"unit":{"description":"`money`, an amount in the answer's `currency_code`; `count`, a whole\nnumber; or `percent`, already scaled so that `4.2` is 4.2%.\n","enum":["money","count","percent","days","months"],"example":"money","type":"string"},"window_reading":{"description":"What `at: window` means for this measure — when a breakdown's or a\nfilter's value is read unless the query names an instant: `end`, the\nvalue in force at the end of each window, as a level is read;\n`start`, the value at the start of each window, where the cohort is,\nas churn and retention are read; or `movement`, the value in force\nwhen the money moved, as a flow is read. A rate\nreads as the more specific of its two sides.\n","enum":["end","start","movement"],"example":"end","type":"string"}},"required":["default_lookback_window","description","dimensions","family","filter_keys","id","label","measure_type","object","unit","window_reading"],"type":"object"},"MetricQueryNode":{"additionalProperties":false,"description":"One category of a breakdown, with its own `values`, `timeseries` and, when there is another dimension, `breakdown`, in the same shape as the top of the answer.","properties":{"breakdown":{"$ref":"#/components/schemas/MetricQueryBreakdown"},"category":{"anyOf":[{"type":"null"},{"description":"The value, as text: a country code, a plan id, a movement type, the first day of a truncated date, the lower boundary of a numeric range. Null for `Other`.","type":"string"}]},"label":{"description":"The value as a person reads it: a country's name, a plan's, the period a truncated date begins.","type":"string"},"timeseries":{"items":{"$ref":"#/components/schemas/MetricQueryEntry"},"type":"array"},"values":{"$ref":"#/components/schemas/MetricQueryValues"}},"required":["category","label","values"],"type":"object"},"PropertyDefinition":{"additionalProperties":false,"description":"A property definition says what a property key holds: the kind of record it\ndescribes, the type of its values, and the source that writes it. A key\nregisters itself when it is first written; declare it ahead of that to\nsettle its type first.\n","example":{"archived":false,"created_at":"2026-02-11T09:20:13Z","id":"0198f1a5-7a8b-7c9d-8e0f-1a2b3c4d5e6f","key":"seats","label":"Seats","object":"property_definition","source_id":"0198f1a3-0c18-7d93-a6e2-1b4f9d7c3a58","subject_type":"customer","value_type":"number"},"properties":{"archived":{"description":"Whether the key has stopped being offered. Its values stay and still read back.","example":false,"type":"boolean"},"created_at":{"description":"When the key was first seen.","example":"2026-02-11T09:20:13Z","format":"date-time","type":"string"},"id":{"description":"The property definition's unique identifier.","example":"0198f1a5-7a8b-7c9d-8e0f-1a2b3c4d5e6f","format":"uuid","type":"string"},"key":{"description":"The key, as written. Immutable once it exists.","example":"seats","maxLength":255,"minLength":1,"type":"string"},"label":{"description":"What a screen calls this property: the name the source that owns it\ngave it, or the key in words — `deal_stage` is `Deal Stage`.\n","example":"Seats","maxLength":255,"minLength":1,"type":"string"},"object":{"description":"The object's type, always `property_definition`.","enum":["property_definition"],"example":"property_definition","type":"string"},"source":{"anyOf":[{"type":"null"},{"allOf":[{"$ref":"#/components/schemas/Integration"}]}],"description":"The source whose namespace this key lives in. Expandable.","example":{"connected_at":"2025-09-08T14:10:05Z","created_at":"2025-09-08T14:09:47Z","current_import_id":"019a0c0f-6b1d-7e3a-8c2f-4d5e6f7a8b9c","id":"0198f1a3-0b7c-7e25-9f41-6a2d8c3e5b19","imported_at":"2025-09-08T14:26:51Z","is_source":true,"name":"Stripe","object":"integration","priority":2,"project_id":"0198f1a2-6c52-7b18-8e3f-2a9c4d6b1e07","provider_account_id":"acct_1QfR3sT8uVw2XyZa","provider_code":"stripe","sandbox":false,"slug":"stripe","webhook_installed_at":"2025-09-08T14:10:06Z","webhook_url":"https://api.summerbase.com/webhooks/integrations/0198f1a3-0b7c-7e25-9f41-6a2d8c3e5b19?c=us1"}},"source_id":{"description":"The source whose namespace this key lives in.","example":"0198f1a3-0c18-7d93-a6e2-1b4f9d7c3a58","format":"uuid","type":"string"},"subject_type":{"description":"What kind of record this key describes.","enum":["customer","subscription","subscription_item","plan","price"],"example":"customer","type":"string"},"value_type":{"description":"What values of this key hold, fixed by the first one written.","enum":["string","number","boolean","datetime"],"example":"number","type":"string"}},"required":["archived","created_at","id","key","label","object","source_id","subject_type","value_type"],"type":"object"},"UpdatePriceResult":{"additionalProperties":false,"description":"A price is what a subscription item is on, in the source it lives in: a\nStripe price, or a plan in a billing system that has no prices. Every\nprice is on one plan, which groups the prices of one thing you sell —\nmonthly and annual, one per currency, or the same plan in two billing\nsystems. A price is created before any subscription item names it.\n","example":{"archived":false,"created_at":"2025-09-08T14:12:30Z","currency_code":"USD","id":"0198f1a4-2e5f-7b80-a1c2-3d4e5f6a7b8c","identities":["stripe:price_1PqR8sT2uV4wX6yZ"],"interval":"month","interval_count":1,"name":"Growth monthly","object":"price","owner_source_id":"0198f1a3-0b7c-7e25-9f41-6a2d8c3e5b19","plan_id":"0198f1a4-1d2e-7a40-9b31-5c6d7e8f9a0b","pricing_model":"fixed","properties":{"tier":"mid"},"properties_by_source":{"backend":{"tier":"mid"}},"tax_behavior":"exclusive","unit_amount":"50.00"},"properties":{"archived":{"description":"Whether the price has stopped being offered. Existing subscriptions keep\nit — what somebody paid for last year does not stop being true when you\nwithdraw the price.\n","example":false,"type":"boolean"},"created_at":{"description":"When the price was first seen.","example":"2025-09-08T14:12:30Z","format":"date-time","type":"string"},"currency_code":{"anyOf":[{"type":"null"},{"maxLength":3,"minLength":3,"type":"string"}],"description":"The currency it is listed in, by its ISO 4217 code.","example":"USD"},"id":{"description":"The price's Summerbase id. Anywhere a price is addressed, one of its `identities` works too.","example":"0198f1a4-2e5f-7b80-a1c2-3d4e5f6a7b8c","format":"uuid","type":"string"},"identities":{"anyOf":[{"type":"null"},{"items":{"type":"string"},"type":"array"}],"description":"What each source calls this price, as `source:id`. Any entry addresses it — `stripe:cus_123`.","example":["stripe:price_1PqR8sT2uV4wX6yZ"]},"interval":{"anyOf":[{"type":"null"},{"enum":["day","week","month","year"],"type":"string"}],"description":"The unit it bills in. None for a price charged once.","example":"month"},"interval_count":{"anyOf":[{"type":"null"},{"minimum":1,"type":"integer"}],"description":"How many intervals between charges.","example":1},"name":{"anyOf":[{"type":"null"},{"maxLength":255,"minLength":1,"type":"string"}],"description":"What the price is called in the source it lives in, when it has a name\nof its own there — a Stripe price's nickname. Most have none, and are\ntold apart by their terms.\n","example":"Growth monthly"},"object":{"description":"The object's type, always `price`.","enum":["price"],"example":"price","type":"string"},"owner_source":{"anyOf":[{"type":"null"},{"allOf":[{"$ref":"#/components/schemas/Integration"}]}],"description":"The source the price came from. Expandable.","example":{"connected_at":"2025-09-08T14:10:05Z","created_at":"2025-09-08T14:09:47Z","current_import_id":"019a0c0f-6b1d-7e3a-8c2f-4d5e6f7a8b9c","id":"0198f1a3-0b7c-7e25-9f41-6a2d8c3e5b19","imported_at":"2025-09-08T14:26:51Z","is_source":true,"name":"Stripe","object":"integration","priority":2,"project_id":"0198f1a2-6c52-7b18-8e3f-2a9c4d6b1e07","provider_account_id":"acct_1QfR3sT8uVw2XyZa","provider_code":"stripe","sandbox":false,"slug":"stripe","webhook_installed_at":"2025-09-08T14:10:06Z","webhook_url":"https://api.summerbase.com/webhooks/integrations/0198f1a3-0b7c-7e25-9f41-6a2d8c3e5b19?c=us1"}},"owner_source_id":{"description":"The source the price came from.","example":"0198f1a3-0b7c-7e25-9f41-6a2d8c3e5b19","format":"uuid","type":"string"},"plan":{"anyOf":[{"type":"null"},{"allOf":[{"$ref":"#/components/schemas/Plan"}]}],"description":"The plan the price is on. Expandable.","example":{"archived":false,"created_at":"2025-09-08T14:12:30Z","id":"0198f1a4-1d2e-7a40-9b31-5c6d7e8f9a0b","identities":["stripe:prod_QhR2sT4uV6wX8y"],"name":"Growth","object":"plan","price_count":2,"properties":{"tier":"mid"},"properties_by_source":{"backend":{"tier":"mid"}}}},"plan_id":{"description":"The plan the price is on.","example":"0198f1a4-1d2e-7a40-9b31-5c6d7e8f9a0b","format":"uuid","type":"string"},"pricing_model":{"anyOf":[{"type":"null"},{"enum":["fixed","tiered","metered"],"type":"string"}],"description":"How an item's unit amount is arrived at on this price.","example":"fixed"},"properties":{"anyOf":[{"type":"null"},{"additionalProperties":{"anyOf":[{"type":"string"},{"type":"number"},{"type":"boolean"}]},"type":"object"}],"description":"**Your** source's properties on this price, as they stand now. What you write here is what you read back — another source's values never appear in it and can never be overwritten through it. `properties_by_source` has everyone's. When you have written a key through several of this price's ids, its value is the most recent change any of them made.","example":{"tier":"mid"}},"properties_by_source":{"anyOf":[{"type":"null"},{"additionalProperties":{"additionalProperties":{"anyOf":[{"type":"string"},{"type":"number"},{"type":"boolean"}]},"description":"Property keys and their current values.","type":"object"},"type":"object"}],"description":"Every source's properties on this price, keyed by source slug — including your own. Read-only: a write only ever reaches the source it is attributed to.","example":{"backend":{"tier":"mid"}}},"response_metadata":{"anyOf":[{"type":"null"},{"additionalProperties":false,"properties":{"warnings":{"description":"Parts of this write that were skipped — a property whose value did not\nfit its type — and things it did that were worth saying, like merging\ntwo customers. The rest of the write landed. Absent when there is\nnothing to report.\n\nSend `on_warning: \"error\"` to have these refuse the write instead,\nand come back as `error.errors` with the same codes.\n","items":{"$ref":"#/components/schemas/Warning"},"type":"array"}},"type":"object"}],"description":"About this request rather than about the record. Absent when there is nothing to say."},"tax_behavior":{"description":"Whether the amount has tax in it: `inclusive`, `exclusive`, or\n`unspecified` when the price leaves it to the source's tax settings. A\nsubscription item is worth its amount without any tax in it, at the\nrate its customer's invoices are taxed at, whatever this says.\n","enum":["inclusive","exclusive","unspecified"],"example":"exclusive","type":"string"},"unit_amount":{"anyOf":[{"type":"null"},{"format":"decimal","type":"string"}],"description":"What one unit is listed at per interval: for a `metered` price, one unit\nof usage. None for a `tiered` one, whose units cost what their tier says.\nBelow zero for a credit, which Stripe, for one, lists as a one-time\nprice with a negative amount. Includes tax when `tax_behavior` says so.\n","example":"50.00"}},"required":["archived","created_at","id","object","owner_source_id","plan_id","tax_behavior"],"type":"object"},"Warning":{"additionalProperties":false,"properties":{"code":{"description":"What kind of thing was skipped, for a client to branch on.","type":"string"},"field":{"anyOf":[{"type":"null"},{"type":"string"}],"description":"The input it came from, like `properties.seats`."},"message":{"description":"What was skipped and why, in a sentence.","type":"string"}},"required":["code","message"],"type":"object"},"MetricPeriod":{"additionalProperties":false,"properties":{"elapsed":{"description":"With `provisional`: the fraction of the period that had elapsed when the query ran, as a decimal between 0 and 1.","format":"decimal","type":"string"},"ends_at":{"description":"Exclusive.","format":"date-time","type":"string"},"label":{"description":"`Sep 2026`, `Q3 2026`, `15–31 Jan 2026`, `15 Jan – 3 Feb 2026`.","type":"string"},"provisional":{"description":"Present, and true, on the period still in progress, whose numbers are still moving.","type":"boolean"},"starts_at":{"format":"date-time","type":"string"}},"required":["label","starts_at","ends_at"],"type":"object"},"SubscriptionItem":{"additionalProperties":false,"description":"A subscription item is one price on a subscription: a price, a quantity and a\nunit amount, billed every interval. Its plan is its price's. A subscription's amounts are summed from\nits items. Items are usually written with their subscription, as its\n`items`, and can also be changed one at a time.\n","example":{"created_at":"2025-11-17T10:04:03Z","deleted":false,"discount_amount":"0","discount_ends_at":null,"id":"0199a7c2-5b41-7c62-9d3e-6a1f8b2c4d97","identities":["stripe:si_QhT6vW8xY0zA3b"],"interval":"month","interval_count":1,"net_amount":"1250.00","object":"subscription_item","plan_id":"0198f1a4-1d2e-7a40-9b31-5c6d7e8f9a0b","plan_name":"Growth","price_id":"0198f1a4-2e5f-7b80-a1c2-3d4e5f6a7b8c","price_name":"Growth monthly","pricing_model":"fixed","properties":{"seat_type":"standard"},"properties_by_source":{"backend":{"seat_type":"standard"}},"quantity":"25","subscription_id":"0199a7c2-5b3e-7a14-8c6d-2f9e4b7a1c05","unit_amount":"50.00"},"properties":{"created_at":{"description":"When this item was first recorded.","example":"2025-11-17T10:04:03Z","format":"date-time","type":"string"},"deleted":{"description":"Whether the item has come off the subscription. **When** it did is the\n`effective_at` of the write that set this.\n","example":false,"type":"boolean"},"discount_amount":{"description":"What comes off this item's invoice, in the subscription's currency,\nafter every discount the source applies to it has been worked out:\ncoupons at any level, stacked in the source's order, restricted to\nthe products they cover. The item is worth `unit_amount × quantity`\nless this, each interval. Zero is no discount.\n","example":"0","format":"decimal","type":"string"},"discount_ends_at":{"anyOf":[{"type":"null"},{"format":"date-time","type":"string"}],"description":"When the discount, or the earliest part of it, stops applying, if the\nsource knows. Information for a reader: the discount comes off when the\nsource writes a smaller `discount_amount`, not when this passes.\n"},"id":{"description":"The item's Summerbase id. Anywhere an item is addressed, one of its `identities` works too.","example":"0199a7c2-5b41-7c62-9d3e-6a1f8b2c4d97","format":"uuid","type":"string"},"identities":{"anyOf":[{"type":"null"},{"items":{"type":"string"},"type":"array"}],"description":"What each source calls this subscription item, as `source:id`. Any entry addresses it — `stripe:cus_123`.","example":["stripe:si_QhT6vW8xY0zA3b"]},"interval":{"description":"The unit this item bills in.","enum":["day","week","month","year"],"example":"month","type":"string"},"interval_count":{"description":"How many intervals between charges.","example":1,"minimum":1,"type":"integer"},"net_amount":{"anyOf":[{"type":"null"},{"format":"decimal","type":"string"}],"description":"What this item comes to each time it is billed, in its subscription's\ncurrency: `unit_amount` times `quantity`, less `discount_amount`.\n","example":"1250.00"},"object":{"description":"The object's type, always `subscription_item`.","enum":["subscription_item"],"example":"subscription_item","type":"string"},"plan_id":{"anyOf":[{"type":"null"},{"format":"uuid","type":"string"}],"description":"The plan this item is on: its price's.","example":"0198f1a4-1d2e-7a40-9b31-5c6d7e8f9a0b"},"plan_name":{"anyOf":[{"type":"null"},{"type":"string"}],"description":"What the plan this item is on is called: its price's.","example":"Growth"},"price":{"anyOf":[{"type":"null"},{"allOf":[{"$ref":"#/components/schemas/Price"}]}],"description":"The price this item is on, when a source named one. Expandable.","example":{"archived":false,"created_at":"2025-09-08T14:12:30Z","currency_code":"USD","id":"0198f1a4-2e5f-7b80-a1c2-3d4e5f6a7b8c","identities":["stripe:price_1PqR8sT2uV4wX6yZ"],"interval":"month","interval_count":1,"name":"Growth monthly","object":"price","owner_source_id":"0198f1a3-0b7c-7e25-9f41-6a2d8c3e5b19","plan_id":"0198f1a4-1d2e-7a40-9b31-5c6d7e8f9a0b","pricing_model":"fixed","properties":{"tier":"mid"},"properties_by_source":{"backend":{"tier":"mid"}},"tax_behavior":"exclusive","unit_amount":"50.00"}},"price_id":{"anyOf":[{"type":"null"},{"format":"uuid","type":"string"}],"description":"The price this item is on, when a source named one.","example":"0198f1a4-2e5f-7b80-a1c2-3d4e5f6a7b8c"},"price_name":{"anyOf":[{"type":"null"},{"type":"string"}],"description":"What the price this item is on is called, when a source named one.","example":"Growth monthly"},"pricing_model":{"description":"How the unit amount is arrived at.","enum":["fixed","tiered","metered"],"example":"fixed","type":"string"},"properties":{"anyOf":[{"type":"null"},{"additionalProperties":{"anyOf":[{"type":"string"},{"type":"number"},{"type":"boolean"}]},"type":"object"}],"description":"**Your** source's properties on this subscription item, as they stand now. What you write here is what you read back — another source's values never appear in it and can never be overwritten through it. `properties_by_source` has everyone's. When you have written a key through several of this subscription item's ids, its value is the most recent change any of them made.","example":{"seat_type":"standard"}},"properties_by_source":{"anyOf":[{"type":"null"},{"additionalProperties":{"additionalProperties":{"anyOf":[{"type":"string"},{"type":"number"},{"type":"boolean"}]},"description":"Property keys and their current values.","type":"object"},"type":"object"}],"description":"Every source's properties on this subscription item, keyed by source slug — including your own. Read-only: a write only ever reaches the source it is attributed to.","example":{"backend":{"seat_type":"standard"}}},"quantity":{"description":"How many units at this unit amount. A decimal, because a unit is not\nalways a whole seat: a billing system that meters usage sends 1.5 of\nsomething, and rounding it would misstate what the item is worth.\n","example":"25","format":"decimal","type":"string"},"subscription":{"anyOf":[{"type":"null"},{"allOf":[{"$ref":"#/components/schemas/Subscription"}]}],"description":"The subscription this is an item of. Expandable.","example":{"annual_amount":"15000.00","billing_amounts":[{"interval":"month","interval_count":1,"net_amount":"1250.00"}],"currency_code":"USD","current_period_ends_at":"2026-11-01T10:04:00Z","customer_id":"0199a7c2-4e1b-7f38-b9d2-5a6c8e0f1d73","id":"0199a7c2-5b3e-7a14-8c6d-2f9e4b7a1c05","identities":["stripe:sub_1QhT5uV7wX9yZ2aB"],"monthly_amount":"1250.00","object":"subscription","owner_source_id":"0198f1a3-0b7c-7e25-9f41-6a2d8c3e5b19","properties":{"account_manager":"Priya Shah"},"properties_by_source":{"backend":{"account_manager":"Priya Shah"},"stripe":{"contract_id":"C-2025-118"}},"recorded_at":"2025-11-17T10:04:03Z","started_at":"2025-11-17T10:04:00Z","status":"active","trial_ends_at":"2025-12-01T10:04:00Z"}},"subscription_id":{"description":"The subscription this is an item of.","example":"0199a7c2-5b3e-7a14-8c6d-2f9e4b7a1c05","format":"uuid","type":"string"},"unit_amount":{"description":"What one unit costs per interval. Excludes tax and any discount.","example":"50.00","format":"decimal","type":"string"}},"required":["created_at","deleted","discount_amount","id","interval","interval_count","object","pricing_model","quantity","subscription_id","unit_amount"],"type":"object"},"Invitation":{"additionalProperties":false,"description":"An invitation asks somebody, by email address, to join the organization with\na role. They become a member by accepting it through the link it sends,\nwhich works for seven days.\n","example":{"email":"sam@example.com","expires_at":"2026-10-26T15:30:00Z","id":"0199c3e4-2a7b-7f10-9c8d-4e6f1a3b5d27","object":"invitation","role":"writer"},"properties":{"email":{"description":"The address the invitation was sent to.","example":"sam@example.com","maxLength":255,"type":"string"},"expires_at":{"description":"When the invitation stops being accepted.","example":"2026-10-26T15:30:00Z","format":"date-time","type":"string"},"id":{"description":"The invitation's unique identifier.","example":"0199c3e4-2a7b-7f10-9c8d-4e6f1a3b5d27","format":"uuid","type":"string"},"object":{"description":"The object's type, always `invitation`.","enum":["invitation"],"example":"invitation","type":"string"},"role":{"description":"The role accepting the invitation grants: `viewer` reads everything,\n`writer` also changes data, and `admin` also manages integrations, API\nkeys and members.\n","enum":["guest","viewer","writer","admin","owner"],"example":"writer","type":"string"}},"required":["email","expires_at","id","object","role"],"type":"object"},"CreateCustomerResult":{"additionalProperties":false,"description":"A customer is a company or a person that pays you, or might. The record is\nresolved from what every source says about them, and carries the amounts\ncomputed from their subscriptions, such as `mrr` and `status`. The changes\nto those subscriptions are the run rate movements.\n","example":{"arr":"15000.00","churned_at":null,"country_code":"US","created_at":"2025-11-03T16:20:04Z","email":"billing@northwind.example.com","first_paying_at":"2025-12-01T10:04:00Z","first_seen_at":"2025-11-03T16:20:00Z","first_subscribed_at":"2025-11-17T10:04:00Z","id":"0199a7c2-4e1b-7f38-b9d2-5a6c8e0f1d73","identities":["stripe:cus_QX7bN2kLp4Rt9v","backend:acct_4821"],"mrr":"1250.00","name":"Northwind Labs","object":"customer","properties":{"seats":25},"properties_by_source":{"backend":{"seats":25},"stripe":{"email":"billing@northwind.example.com","name":"Northwind Labs"}},"signed_up_at":"2025-11-03T16:20:00Z","status":"paying"},"properties":{"arr":{"anyOf":[{"type":"null"},{"format":"decimal","type":"string"}],"description":"What this customer is worth a year, in the project's reporting currency,\nat today's rate: `mrr`, rounded to the cent, times twelve.\n","example":"15000.00"},"churned_at":{"anyOf":[{"type":"null"},{"format":"date-time","type":"string"}],"description":"When the current churn began — the instant `status` became `churned`.\nNull while the customer is trialing or active, and cleared when they\ncome back.\n"},"country":{"anyOf":[{"type":"null"},{"allOf":[{"$ref":"#/components/schemas/Country"}]}],"description":"The country the customer is billed in. Expandable.","example":{"code":"US","name":"United States","object":"country"}},"country_code":{"anyOf":[{"type":"null"},{"type":"string"}],"description":"The country the customer is billed in, by its ISO 3166-1 alpha-2 code —\nthe `country_code` property, resolved the same way as `name`, from the\nhighest-priority source whose value is a country's code. Expand `country`\nfor its name.\n","example":"US"},"created_at":{"description":"When Summerbase first recorded the customer.","example":"2025-11-03T16:20:04Z","format":"date-time","type":"string"},"email":{"anyOf":[{"type":"null"},{"maxLength":255,"type":"string"}],"description":"The customer's address, resolved the same way as `name`.","example":"billing@northwind.example.com"},"erasures":{"anyOf":[{"type":"null"},{"items":{"$ref":"#/components/schemas/CustomerErasure"},"type":"array"}],"description":"Every erasure of their personal data, oldest first. Expandable.","example":[{"customer_id":"0199a7c2-4e1b-7f38-b9d2-5a6c8e0f1d73","erased_properties":["customer.properties.backend.phone","customer.properties.stripe.name"],"id":"019a1f04-7c2d-7b3e-8a5f-1e2d3c4b5a69","object":"customer_erasure","requested_at":"2026-10-25T09:12:40Z"}]},"first_paying_at":{"anyOf":[{"type":"null"},{"format":"date-time","type":"string"}],"description":"The first instant this customer had revenue — when `status` first became\n`paying`, and what cohort analysis groups by. Set once and kept through\nchurn and reactivation. Which month it falls in depends on the timezone\nbeing reported in.\n","example":"2025-12-01T10:04:00Z"},"first_seen_at":{"anyOf":[{"type":"null"},{"format":"date-time","type":"string"}],"description":"The earliest instant anything was asserted about this customer, their\nsubscriptions or their items — the first thing we were told. Set once.\n","example":"2025-11-03T16:20:00Z"},"first_subscribed_at":{"anyOf":[{"type":"null"},{"format":"date-time","type":"string"}],"description":"The first instant one of this customer's subscriptions was live, free or\npaying — when `status` first became one of the two. Set once and kept\nthrough churn.\n","example":"2025-11-17T10:04:00Z"},"id":{"description":"The customer's Summerbase id. Anywhere a customer is addressed, one of its `identities` works too.","example":"0199a7c2-4e1b-7f38-b9d2-5a6c8e0f1d73","format":"uuid","type":"string"},"identities":{"anyOf":[{"type":"null"},{"items":{"type":"string"},"type":"array"}],"description":"What each source calls this customer, as `source:id`. Any entry addresses it — `stripe:cus_123`.","example":["stripe:cus_QX7bN2kLp4Rt9v","backend:acct_4821"]},"mrr":{"anyOf":[{"type":"null"},{"format":"decimal","type":"string"}],"description":"What this customer is worth a month, in the project's reporting currency,\nat today's rate, rounded to the cent.\n\nEvery kind of revenue counts, each change converted from the currency it was\nbilled in, so this amount moves when a rate does and when the project's\nreporting currency changes. The same amount a filter on `customer.mrr`\ncompares and a list sorted by `mrr` orders by.\n","example":"1250.00"},"name":{"anyOf":[{"type":"null"},{"maxLength":255,"type":"string"}],"description":"What the customer is called — the `name` property, resolved to the\nhighest-priority source that has one. Write `properties.name` to change it.\n","example":"Northwind Labs"},"object":{"description":"The object's type, always `customer`.","enum":["customer"],"example":"customer","type":"string"},"properties":{"anyOf":[{"type":"null"},{"additionalProperties":{"anyOf":[{"type":"string"},{"type":"number"},{"type":"boolean"}]},"type":"object"}],"description":"**Your** source's properties on this customer, as they stand now. What you write here is what you read back — another source's values never appear in it and can never be overwritten through it. `properties_by_source` has everyone's. When you have written a key through several of this customer's ids, its value is the most recent change any of them made.","example":{"seats":25}},"properties_by_source":{"anyOf":[{"type":"null"},{"additionalProperties":{"additionalProperties":{"anyOf":[{"type":"string"},{"type":"number"},{"type":"boolean"}]},"description":"Property keys and their current values.","type":"object"},"type":"object"}],"description":"Every source's properties on this customer, keyed by source slug — including your own. Read-only: a write only ever reaches the source it is attributed to.","example":{"backend":{"seats":25},"stripe":{"email":"billing@northwind.example.com","name":"Northwind Labs"}}},"response_metadata":{"anyOf":[{"type":"null"},{"additionalProperties":false,"properties":{"warnings":{"description":"Parts of this write that were skipped — a property whose value did not\nfit its type — and things it did that were worth saying, like merging\ntwo customers. The rest of the write landed. Absent when there is\nnothing to report.\n\nSend `on_warning: \"error\"` to have these refuse the write instead,\nand come back as `error.errors` with the same codes.\n","items":{"$ref":"#/components/schemas/Warning"},"type":"array"}},"type":"object"}],"description":"About this request rather than about the record. Absent when there is nothing to say."},"signed_up_at":{"anyOf":[{"type":"null"},{"format":"date-time","type":"string"}],"description":"When the customer signed up for the product — the `signed_up_at`\nproperty, resolved the same way as `name`. Write\n`properties.signed_up_at` to set it; a billing provider's source writes\nthe moment it first saw the customer, and a project's own source\noutranks it. A customer who has signed up and never subscribed is\n`trialing`.\n","example":"2025-11-03T16:20:00Z"},"status":{"description":"Where the customer stands with us, derived from what is live now: `paying`,\n`free`, `churned`, `trialing` or `lead`. A subscription is live while its\nstatus is `active`; paying and free are told apart by `mrr`, so `paying`\nmeans on a paid plan, whether or not an invoice has been settled. A customer\nwith nothing live who has subscribed before is `churned`; one on a trial, or\nwho has signed up and never subscribed, is `trialing`; anyone else is a\n`lead`. A change undone inside the project's collapse window never happened\nto the status either.\n","enum":["paying","free","churned","trialing","lead"],"example":"paying","type":"string"},"subscriptions":{"anyOf":[{"type":"null"},{"items":{"$ref":"#/components/schemas/Subscription"},"type":"array"}],"description":"Everything they are subscribed to, from every source. Expandable.","example":[{"annual_amount":"15000.00","billing_amounts":[{"interval":"month","interval_count":1,"net_amount":"1250.00"}],"currency_code":"USD","current_period_ends_at":"2026-11-01T10:04:00Z","customer_id":"0199a7c2-4e1b-7f38-b9d2-5a6c8e0f1d73","id":"0199a7c2-5b3e-7a14-8c6d-2f9e4b7a1c05","identities":["stripe:sub_1QhT5uV7wX9yZ2aB"],"monthly_amount":"1250.00","object":"subscription","owner_source_id":"0198f1a3-0b7c-7e25-9f41-6a2d8c3e5b19","properties":{"account_manager":"Priya Shah"},"properties_by_source":{"backend":{"account_manager":"Priya Shah"},"stripe":{"contract_id":"C-2025-118"}},"recorded_at":"2025-11-17T10:04:03Z","started_at":"2025-11-17T10:04:00Z","status":"active","trial_ends_at":"2025-12-01T10:04:00Z"}]}},"required":["created_at","id","object","status"],"type":"object"},"MetricMeasure":{"additionalProperties":false,"properties":{"description":{"description":"What the number is, in a sentence.","type":"string"},"key":{"description":"The key it comes under in `values`, an entry and a snapshot: `mrr`, `mrr_movement`, `customer_churn`, and so on — `GET /v1/metrics/measures` lists them.","type":"string"},"label":{"description":"What to call it: `Revenue`, `Run rate movement`, `Lost revenue`.","type":"string"},"unit":{"description":"`money`, an amount in the answer's `currency_code`; `count`, a whole number; or `percent`, already scaled so that `4.2` is 4.2%.","enum":["money","count","percent","days","months"],"type":"string"}},"required":["description","key","label","unit"],"type":"object"},"CustomerErasure":{"additionalProperties":false,"description":"A customer erasure records one erasure of a customer's personal data: the\nproperties removed, who asked, and when Summerbase finished\nremoving every copy. It names what was erased and never holds the erased\nvalues.\n","example":{"completed_at":null,"customer_id":"0199a7c2-4e1b-7f38-b9d2-5a6c8e0f1d73","erased_properties":["customer.properties.backend.phone","customer.properties.stripe.name"],"id":"019a1f04-7c2d-7b3e-8a5f-1e2d3c4b5a69","object":"customer_erasure","requested_at":"2026-10-25T09:12:40Z","requested_by_id":null},"properties":{"completed_at":{"anyOf":[{"type":"null"},{"format":"date-time","type":"string"}],"description":"When every copy Summerbase keeps had been removed too, or empty while that\nis still to happen — within a day of `requested_at`.\n"},"customer":{"anyOf":[{"type":"null"},{"allOf":[{"$ref":"#/components/schemas/Customer"}]}],"description":"The customer whose data was erased. Expandable.","example":{"arr":"15000.00","country_code":"US","created_at":"2025-11-03T16:20:04Z","email":"billing@northwind.example.com","first_paying_at":"2025-12-01T10:04:00Z","first_seen_at":"2025-11-03T16:20:00Z","first_subscribed_at":"2025-11-17T10:04:00Z","id":"0199a7c2-4e1b-7f38-b9d2-5a6c8e0f1d73","identities":["stripe:cus_QX7bN2kLp4Rt9v","backend:acct_4821"],"mrr":"1250.00","name":"Northwind Labs","object":"customer","properties":{"seats":25},"properties_by_source":{"backend":{"seats":25},"stripe":{"email":"billing@northwind.example.com","name":"Northwind Labs"}},"signed_up_at":"2025-11-03T16:20:00Z","status":"paying"}},"customer_id":{"description":"The customer whose data was erased.","example":"0199a7c2-4e1b-7f38-b9d2-5a6c8e0f1d73","format":"uuid","type":"string"},"erased_properties":{"description":"Each property erased, as `record.properties.source.key`. A property is\nlisted once however many values it held.\n","example":["customer.properties.backend.phone","customer.properties.stripe.name"],"items":{"type":"string"},"type":"array"},"id":{"description":"The erasure's unique identifier.","example":"019a1f04-7c2d-7b3e-8a5f-1e2d3c4b5a69","format":"uuid","type":"string"},"object":{"description":"The object's type, always `customer_erasure`.","enum":["customer_erasure"],"example":"customer_erasure","type":"string"},"requested_at":{"description":"When the erasure was asked for. Erased values were gone from every answer moments after.","example":"2026-10-25T09:12:40Z","format":"date-time","type":"string"},"requested_by":{"anyOf":[{"type":"null"},{"allOf":[{"$ref":"#/components/schemas/User"}]}],"description":"Who asked for the erasure, when a person did. Empty when an API key did. Expandable.","example":{"created_at":"2025-09-08T13:58:02Z","email":"maya@example.com","id":"0198f1a2-6b10-7d2a-b4c5-9e8f7a6b5c41","name":"Maya Chen","oauth_provider":"google","object":"user"}},"requested_by_id":{"anyOf":[{"type":"null"},{"format":"uuid","type":"string"}],"description":"Who asked for the erasure, when a person did. Empty when an API key did."}},"required":["customer_id","erased_properties","id","object","requested_at"],"type":"object"},"MetricQueryEntry":{"additionalProperties":false,"description":"One period: its label, and each measure's value under its key.","properties":{"starting_mrr":{"anyOf":[{"type":"null"},{"format":"decimal","type":"string"}],"description":"`starting_mrr` is `mrr` as it stood at the start of each window. A decimal as text, or null where there is no answer."},"starting_arr":{"anyOf":[{"type":"null"},{"format":"decimal","type":"string"}],"description":"`starting_arr` is `arr` as it stood at the start of each window. A decimal as text, or null where there is no answer."},"churned_mrr":{"anyOf":[{"type":"null"},{"format":"decimal","type":"string"}],"description":"`churned_mrr` is the MRR lost in each window to customers who stopped paying, as a positive amount, including those who started inside it. `starting_mrr_lost` counts only those paying at its start. A decimal as text, or null where there is no answer."},"starting_mrr_lost":{"anyOf":[{"type":"null"},{"format":"decimal","type":"string"}],"description":"`starting_mrr_lost` is how much less per month the customers paying at the start of each window pay at its end: churn and contraction. A decimal as text, or null where there is no answer."},"customer_lifespan":{"anyOf":[{"type":"null"},{"format":"decimal","type":"string"}],"description":"`customer_lifespan` is how many months a paying customer stays on average, at each window's rate of churn. Formula: one divided by `monthly_customer_churn`. A decimal as text, or null where there is no answer."},"paying_customers":{"anyOf":[{"type":"null"},{"format":"decimal","type":"string"}],"description":"`paying_customers` is the number of customers with revenue at the end of each window. A decimal as text, or null where there is no answer."},"reactivation_arr":{"anyOf":[{"type":"null"},{"format":"decimal","type":"string"}],"description":"`reactivation_arr` is the ARR added in each window by customers who started paying again after having churned. A decimal as text, or null where there is no answer."},"arr":{"anyOf":[{"type":"null"},{"format":"decimal","type":"string"}],"description":"`arr` is the Annual Run Rate: revenue per year from every active subscription at the end of each window, after discounts, recurring and usage alike. A decimal as text, or null where there is no answer."},"net_new_paying_customers":{"anyOf":[{"type":"null"},{"format":"decimal","type":"string"}],"description":"`net_new_paying_customers` is how much `paying_customers` changed over each window, except that under a breakdown or a filter a customer who moved category is neither. Formula: `new_paying_customers` plus `reactivated_paying_customers` minus `churned_paying_customers`. A decimal as text, or null where there is no answer."},"signup_to_paid_conversion":{"anyOf":[{"type":"null"},{"format":"decimal","type":"string"}],"description":"`signup_to_paid_conversion` is the share of each window's `new_signups` who have become paying since. A recent window reads low and rises as its signups convert. Formula: `signups_converted_to_paid` divided by `new_signups`. A decimal as text, or null where there is no answer."},"mrr_growth":{"anyOf":[{"type":"null"},{"format":"decimal","type":"string"}],"description":"`mrr_growth` is how much `mrr` grew or fell over each window, as a percentage. Formula: `mrr_movement` divided by `starting_mrr`. A decimal as text, or null where there is no answer."},"lead_to_paid_conversion":{"anyOf":[{"type":"null"},{"format":"decimal","type":"string"}],"description":"`lead_to_paid_conversion` is the share of each window's `new_leads` who have become paying since. A recent window reads low and rises as its leads convert. Formula: `leads_converted_to_paid` divided by `new_leads`. A decimal as text, or null where there is no answer."},"starting_mrr_gained":{"anyOf":[{"type":"null"},{"format":"decimal","type":"string"}],"description":"`starting_mrr_gained` is how much more per month the customers paying at the start of each window pay at its end: expansion. A decimal as text, or null where there is no answer."},"reactivation_mrr":{"anyOf":[{"type":"null"},{"format":"decimal","type":"string"}],"description":"`reactivation_mrr` is the MRR added in each window by customers who started paying again after having churned. A decimal as text, or null where there is no answer."},"starting_paying_customers_retained":{"anyOf":[{"type":"null"},{"format":"decimal","type":"string"}],"description":"`starting_paying_customers_retained` is the number of customers with revenue at both the start and the end of each window. A decimal as text, or null where there is no answer."},"contraction_arr":{"anyOf":[{"type":"null"},{"format":"decimal","type":"string"}],"description":"`contraction_arr` is the ARR lost in each window to customers who came to pay less and still pay something, as a positive amount. A decimal as text, or null where there is no answer."},"new_mrr":{"anyOf":[{"type":"null"},{"format":"decimal","type":"string"}],"description":"`new_mrr` is the MRR added in each window by customers who started paying for the first time, whether or not they still pay at its end. A decimal as text, or null where there is no answer."},"monthly_customer_churn":{"anyOf":[{"type":"null"},{"format":"decimal","type":"string"}],"description":"`monthly_customer_churn` is `customer_churn` restated as a rate per month, whatever the length of each window. A decimal as text, or null where there is no answer."},"run_rate_movers":{"anyOf":[{"type":"null"},{"format":"decimal","type":"string"}],"description":"`run_rate_movers` is the number of customers whose revenue changed over each window. A decimal as text, or null where there is no answer."},"customers":{"anyOf":[{"type":"null"},{"format":"decimal","type":"string"}],"description":"`customers` is every customer known at the end of each window, whatever their status: leads, free, trialing, paying and churned. A decimal as text, or null where there is no answer."},"signups_converted_to_paid":{"anyOf":[{"type":"null"},{"format":"decimal","type":"string"}],"description":"`signups_converted_to_paid` is the number of each window's `new_signups` who have become paying since. A recent window reads low and rises as its signups convert. A decimal as text, or null where there is no answer."},"arpa_per_year":{"anyOf":[{"type":"null"},{"format":"decimal","type":"string"}],"description":"`arpa_per_year` is the Average Revenue Per Account per year: what a paying customer pays on average at the end of each window. Formula: `arr` divided by `paying_customers`. A decimal as text, or null where there is no answer."},"gross_revenue_churn":{"anyOf":[{"type":"null"},{"format":"decimal","type":"string"}],"description":"`gross_revenue_churn` is the share of revenue lost to churn and contraction over each window. Formula: `starting_mrr_lost` divided by `starting_mrr`, taken at the exchange rate of each window's end rather than its start, as `starting_mrr_lost` is. A decimal as text, or null where there is no answer."},"net_revenue_retention":{"anyOf":[{"type":"null"},{"format":"decimal","type":"string"}],"description":"`net_revenue_retention` is the share of revenue kept over each window, expansion included; above 100% when existing customers grew. Formula: `starting_mrr_retained` divided by `starting_mrr`, taken at the exchange rate of each window's end rather than its start, as `starting_mrr_retained` is. A decimal as text, or null where there is no answer."},"exchange_rate_arr":{"anyOf":[{"type":"null"},{"format":"decimal","type":"string"}],"description":"`exchange_rate_arr` is how much `arr` changed in each window because exchange rates moved, with nobody paying differently in their own currency: positive when the currencies customers pay in strengthened against the reporting currency, and zero for a project whose customers all pay in it. A decimal as text, or null where there is no answer."},"new_paying_customers":{"anyOf":[{"type":"null"},{"format":"decimal","type":"string"}],"description":"`new_paying_customers` is the number of customers who started paying for the first time in each window, whether or not they still pay at its end. A decimal as text, or null where there is no answer."},"mrr":{"anyOf":[{"type":"null"},{"format":"decimal","type":"string"}],"description":"`mrr` is the Monthly Run Rate: revenue per month from every active subscription at the end of each window, after discounts, recurring and usage alike. A decimal as text, or null where there is no answer."},"net_new_customers":{"anyOf":[{"type":"null"},{"format":"decimal","type":"string"}],"description":"`net_new_customers` is how much `customers` changed over each window. A decimal as text, or null where there is no answer."},"arr_growth":{"anyOf":[{"type":"null"},{"format":"decimal","type":"string"}],"description":"`arr_growth` is how much `arr` grew or fell over each window, as a percentage. Formula: `arr_movement` divided by `starting_arr`. A decimal as text, or null where there is no answer."},"customer_retention":{"anyOf":[{"type":"null"},{"format":"decimal","type":"string"}],"description":"`customer_retention` is the share of paying customers kept over each window. Formula: `starting_paying_customers_retained` divided by `starting_paying_customers`. A decimal as text, or null where there is no answer."},"starting_customers":{"anyOf":[{"type":"null"},{"format":"decimal","type":"string"}],"description":"`starting_customers` is `customers` as it stood at the start of each window. A decimal as text, or null where there is no answer."},"starting_arr_gained":{"anyOf":[{"type":"null"},{"format":"decimal","type":"string"}],"description":"`starting_arr_gained` is how much more per year the customers paying at the start of each window pay at its end: expansion. A decimal as text, or null where there is no answer."},"arpa_per_month":{"anyOf":[{"type":"null"},{"format":"decimal","type":"string"}],"description":"`arpa_per_month` is the Average Revenue Per Account per month: what a paying customer pays on average at the end of each window. Formula: `mrr` divided by `paying_customers`. A decimal as text, or null where there is no answer."},"starting_paying_customers":{"anyOf":[{"type":"null"},{"format":"decimal","type":"string"}],"description":"`starting_paying_customers` is `paying_customers` as it stood at the start of each window. A decimal as text, or null where there is no answer."},"ltv":{"anyOf":[{"type":"null"},{"format":"decimal","type":"string"}],"description":"`ltv` is the Lifetime Value: what a paying customer pays over the time they stay, before any cost. Formula: `arpa_per_month` times `customer_lifespan`. A decimal as text, or null where there is no answer."},"starting_mrr_retained":{"anyOf":[{"type":"null"},{"format":"decimal","type":"string"}],"description":"`starting_mrr_retained` is what the customers paying at the start of each window pay per month at its end, expansion included, so it exceeds `starting_mrr` when they grew. A decimal as text, or null where there is no answer."},"starting_arr_retained":{"anyOf":[{"type":"null"},{"format":"decimal","type":"string"}],"description":"`starting_arr_retained` is what the customers paying at the start of each window pay per year at its end, expansion included, so it exceeds `starting_arr` when they grew. A decimal as text, or null where there is no answer."},"new_leads":{"anyOf":[{"type":"null"},{"format":"decimal","type":"string"}],"description":"`new_leads` is the number of customers first seen in each window as leads. A decimal as text, or null where there is no answer."},"contraction_mrr":{"anyOf":[{"type":"null"},{"format":"decimal","type":"string"}],"description":"`contraction_mrr` is the MRR lost in each window to customers who came to pay less and still pay something, as a positive amount. A decimal as text, or null where there is no answer."},"new_arr":{"anyOf":[{"type":"null"},{"format":"decimal","type":"string"}],"description":"`new_arr` is the ARR added in each window by customers who started paying for the first time, whether or not they still pay at its end. A decimal as text, or null where there is no answer."},"median_signup_to_paid_time":{"anyOf":[{"type":"null"},{"format":"decimal","type":"string"}],"description":"`median_signup_to_paid_time` is the median number of days from signing up to first paying, of the customers who became paying in each window. A decimal as text, or null where there is no answer."},"arr_movement":{"anyOf":[{"type":"null"},{"format":"decimal","type":"string"}],"description":"`arr_movement` is how much `arr` changed over each window: what was new, expanded and reactivated, minus what contracted and churned, plus what exchange rates moved it by. Formula: `new_arr` plus `expansion_arr` plus `reactivation_arr` minus `contraction_arr` minus `churned_arr` plus `exchange_rate_arr`, and under a filter or a breakdown, what entered the category less what left it. A decimal as text, or null where there is no answer."},"gross_revenue_retention":{"anyOf":[{"type":"null"},{"format":"decimal","type":"string"}],"description":"`gross_revenue_retention` is the share of revenue kept over each window, before any expansion. Formula: `starting_mrr` minus `starting_mrr_lost`, divided by `starting_mrr`, with `starting_mrr` taken at the exchange rate of each window's end rather than its start, as `starting_mrr_lost` is. A decimal as text, or null where there is no answer."},"starting_paying_customers_lost":{"anyOf":[{"type":"null"},{"format":"decimal","type":"string"}],"description":"`starting_paying_customers_lost` is the number of customers with revenue at the start of each window who have none at its end. A decimal as text, or null where there is no answer."},"exchange_rate_mrr":{"anyOf":[{"type":"null"},{"format":"decimal","type":"string"}],"description":"`exchange_rate_mrr` is how much `mrr` changed in each window because exchange rates moved, with nobody paying differently in their own currency: positive when the currencies customers pay in strengthened against the reporting currency, and zero for a project whose customers all pay in it. A decimal as text, or null where there is no answer."},"new_signups":{"anyOf":[{"type":"null"},{"format":"decimal","type":"string"}],"description":"`new_signups` is the number of customers who signed up in each window. A decimal as text, or null where there is no answer."},"median_lead_to_paid_time":{"anyOf":[{"type":"null"},{"format":"decimal","type":"string"}],"description":"`median_lead_to_paid_time` is the median number of days from first being seen as a lead to first paying, of the customers who became paying in each window. A decimal as text, or null where there is no answer."},"churned_paying_customers":{"anyOf":[{"type":"null"},{"format":"decimal","type":"string"}],"description":"`churned_paying_customers` is how many times customers stopped paying in each window, including those who started inside it; a customer who churned twice counts twice. `starting_paying_customers_lost` counts only those paying at its start. A decimal as text, or null where there is no answer."},"starting_arr_lost":{"anyOf":[{"type":"null"},{"format":"decimal","type":"string"}],"description":"`starting_arr_lost` is how much less per year the customers paying at the start of each window pay at its end: churn and contraction. A decimal as text, or null where there is no answer."},"customer_churn":{"anyOf":[{"type":"null"},{"format":"decimal","type":"string"}],"description":"`customer_churn` is the share of paying customers lost over each window. Formula: `starting_paying_customers_lost` divided by `starting_paying_customers`. A decimal as text, or null where there is no answer."},"net_revenue_churn":{"anyOf":[{"type":"null"},{"format":"decimal","type":"string"}],"description":"`net_revenue_churn` is the share of revenue lost over each window after expansion is counted; negative when existing customers grew. Formula: `starting_mrr_lost` minus `starting_mrr_gained`, divided by `starting_mrr`, taken at the exchange rate of each window's end rather than its start, as the other two are. A decimal as text, or null where there is no answer."},"leads_converted_to_paid":{"anyOf":[{"type":"null"},{"format":"decimal","type":"string"}],"description":"`leads_converted_to_paid` is the number of each window's `new_leads` who have become paying since. A recent window reads low and rises as its leads convert. A decimal as text, or null where there is no answer."},"period":{"description":"The period's label.","type":"string"},"reactivated_paying_customers":{"anyOf":[{"type":"null"},{"format":"decimal","type":"string"}],"description":"`reactivated_paying_customers` is how many times customers started paying again in each window after having churned, whether or not they still pay at its end; a customer who came back twice counts twice. A decimal as text, or null where there is no answer."},"expansion_arr":{"anyOf":[{"type":"null"},{"format":"decimal","type":"string"}],"description":"`expansion_arr` is the ARR added in each window by paying customers who came to pay more. A decimal as text, or null where there is no answer."},"expansion_mrr":{"anyOf":[{"type":"null"},{"format":"decimal","type":"string"}],"description":"`expansion_mrr` is the MRR added in each window by paying customers who came to pay more. A decimal as text, or null where there is no answer."},"churned_arr":{"anyOf":[{"type":"null"},{"format":"decimal","type":"string"}],"description":"`churned_arr` is the ARR lost in each window to customers who stopped paying, as a positive amount, including those who started inside it. `starting_arr_lost` counts only those paying at its start. A decimal as text, or null where there is no answer."},"mrr_movement":{"anyOf":[{"type":"null"},{"format":"decimal","type":"string"}],"description":"`mrr_movement` is how much `mrr` changed over each window: what was new, expanded and reactivated, minus what contracted and churned, plus what exchange rates moved it by. Formula: `new_mrr` plus `expansion_mrr` plus `reactivation_mrr` minus `contraction_mrr` minus `churned_mrr` plus `exchange_rate_mrr`, and under a filter or a breakdown, what entered the category less what left it. A decimal as text, or null where there is no answer."}},"required":["period"],"type":"object"},"Timezone":{"additionalProperties":false,"description":"A timezone a project's reporting or a metrics query can be read in, by its\nIANA name.\n","example":{"area":"Eastern (most areas)","country_code":"US","name":"America/New_York","object":"timezone"},"properties":{"area":{"anyOf":[{"type":"null"},{"type":"string"}],"description":"Which part of its country it covers, where the country has several:\n`Eastern (most areas)`. None where the country has one.\n","example":"Eastern (most areas)"},"country_code":{"anyOf":[{"type":"null"},{"type":"string"}],"description":"The country it is in, by ISO 3166-1 alpha-2 code: `DK`. None for `UTC`.\n","example":"US"},"name":{"description":"Its IANA name, which is what a `timezone` is sent as: `Europe/Copenhagen`,\n`America/New_York`, `UTC`.\n","example":"America/New_York","type":"string"},"object":{"description":"The object's type, always `timezone`.","enum":["timezone"],"example":"timezone","type":"string"}},"required":["name","object"],"type":"object"},"CreatePlanResult":{"additionalProperties":false,"description":"A plan is one thing you sell — Pro, Team — and the prices it is billed as:\nmonthly and annual, one per currency, and the prices of the same plan in\nevery billing system you have used. Metrics can be broken down and filtered\nby plan. A plan is created with the first price that names it, or ahead of\ntime, and plans can be renamed and merged without touching the\nsubscriptions on them.\n","example":{"archived":false,"created_at":"2025-09-08T14:12:30Z","id":"0198f1a4-1d2e-7a40-9b31-5c6d7e8f9a0b","identities":["stripe:prod_QhR2sT4uV6wX8y"],"name":"Growth","object":"plan","price_count":2,"properties":{"tier":"mid"},"properties_by_source":{"backend":{"tier":"mid"}}},"properties":{"archived":{"description":"Whether the plan has stopped being offered — the `archived` property,\nresolved to the highest-priority source that says one; Stripe says it\nof a product it has archived. Write `properties.archived` to change it.\nExisting subscriptions keep the plan: what somebody paid for last year\ndoes not stop being true when you withdraw it.\n","example":false,"type":"boolean"},"created_at":{"description":"When the plan was first seen.","example":"2025-09-08T14:12:30Z","format":"date-time","type":"string"},"id":{"description":"The plan's Summerbase id. Anywhere a plan is addressed, one of its `identities` works too.","example":"0198f1a4-1d2e-7a40-9b31-5c6d7e8f9a0b","format":"uuid","type":"string"},"identities":{"anyOf":[{"type":"null"},{"items":{"type":"string"},"type":"array"}],"description":"What each source calls this plan, as `source:id`. Any entry addresses it — `stripe:cus_123`.","example":["stripe:prod_QhR2sT4uV6wX8y"]},"name":{"anyOf":[{"type":"null"},{"maxLength":255,"type":"string"}],"description":"What the plan is called in a filter, a legend or a breakdown — the\n`name` property, resolved to the highest-priority source that has one.\nWrite `properties.name` to change it. Null when no source names it.\n","example":"Growth"},"object":{"description":"The object's type, always `plan`.","enum":["plan"],"example":"plan","type":"string"},"price_count":{"description":"How many prices are on this plan.","example":2,"type":"integer"},"prices":{"anyOf":[{"type":"null"},{"items":{"$ref":"#/components/schemas/Price"},"type":"array"}],"description":"The prices on this plan, in every source. Expandable.","example":[{"archived":false,"created_at":"2025-09-08T14:12:30Z","currency_code":"USD","id":"0198f1a4-2e5f-7b80-a1c2-3d4e5f6a7b8c","identities":["stripe:price_1PqR8sT2uV4wX6yZ"],"interval":"month","interval_count":1,"name":"Growth monthly","object":"price","owner_source_id":"0198f1a3-0b7c-7e25-9f41-6a2d8c3e5b19","plan_id":"0198f1a4-1d2e-7a40-9b31-5c6d7e8f9a0b","pricing_model":"fixed","properties":{"tier":"mid"},"properties_by_source":{"backend":{"tier":"mid"}},"tax_behavior":"exclusive","unit_amount":"50.00"}]},"properties":{"anyOf":[{"type":"null"},{"additionalProperties":{"anyOf":[{"type":"string"},{"type":"number"},{"type":"boolean"}]},"type":"object"}],"description":"**Your** source's properties on this plan, as they stand now. What you write here is what you read back — another source's values never appear in it and can never be overwritten through it. `properties_by_source` has everyone's. When you have written a key through several of this plan's ids, its value is the most recent change any of them made.","example":{"tier":"mid"}},"properties_by_source":{"anyOf":[{"type":"null"},{"additionalProperties":{"additionalProperties":{"anyOf":[{"type":"string"},{"type":"number"},{"type":"boolean"}]},"description":"Property keys and their current values.","type":"object"},"type":"object"}],"description":"Every source's properties on this plan, keyed by source slug — including your own. Read-only: a write only ever reaches the source it is attributed to.","example":{"backend":{"tier":"mid"}}},"response_metadata":{"anyOf":[{"type":"null"},{"additionalProperties":false,"properties":{"warnings":{"description":"Parts of this write that were skipped — a property whose value did not\nfit its type — and things it did that were worth saying, like merging\ntwo customers. The rest of the write landed. Absent when there is\nnothing to report.\n\nSend `on_warning: \"error\"` to have these refuse the write instead,\nand come back as `error.errors` with the same codes.\n","items":{"$ref":"#/components/schemas/Warning"},"type":"array"}},"type":"object"}],"description":"About this request rather than about the record. Absent when there is nothing to say."}},"required":["archived","created_at","id","object","price_count"],"type":"object"},"SubscriptionItemInput":{"additionalProperties":false,"properties":{"discount_amount":{"anyOf":[{"type":"null"},{"format":"decimal","type":"string"}],"description":"What comes off this item's invoice, as a decimal string, every coupon on it worked out. `0` is no discount, and clears one."},"discount_ends_at":{"anyOf":[{"type":"null"},{"format":"date-time","type":"string"}],"description":"When the discount, or the earliest part of it, stops applying, if known. Read with `discount_amount`: sent without it, an amount has no known end."},"id":{"description":"This item's id — a Summerbase id, or a source identity like `stripe:si_123`.","maxLength":320,"minLength":1,"type":"string"},"identities":{"anyOf":[{"type":"null"},{"items":{"type":"string"},"type":"array"}],"description":"What other sources call this item, as `source:id`."},"interval":{"anyOf":[{"type":"null"},{"enum":["day","week","month","year"],"type":"string"}],"description":"The unit the item bills in."},"interval_count":{"anyOf":[{"type":"null"},{"minimum":1,"type":"integer"}],"description":"How many intervals between charges. Two months is `month` with a count of 2."},"price_id":{"anyOf":[{"type":"null"},{"maxLength":320,"type":"string"}],"description":"The price this item is on — a Summerbase id, or a source identity like `stripe:price_123`. It has to exist: create it first, with `PATCH /v1/prices/{id}`. The item's plan is the price's. Leave it out to keep the price the item is on."},"pricing_model":{"anyOf":[{"type":"null"},{"enum":["fixed","tiered","metered"],"type":"string"}],"description":"How the unit amount is arrived at. `fixed` unless said otherwise."},"properties":{"anyOf":[{"type":"null"},{"additionalProperties":{"anyOf":[{"type":"string"},{"type":"number"},{"type":"boolean"},{"$ref":"#/components/schemas/PropertyInput"}]},"type":"object"}],"description":"Properties to record for this item, in your source."},"quantity":{"anyOf":[{"type":"null"},{"format":"decimal","type":"string"}],"description":"How many units at this unit amount. A decimal string; `1.5` is allowed."},"unit_amount":{"anyOf":[{"type":"null"},{"format":"decimal","type":"string"}],"description":"What one unit costs per interval, as a decimal string. Excludes tax and discounts."}},"required":["id"],"type":"object"},"MetricQueryMeta":{"additionalProperties":false,"description":"About the answer: the dates the range came to, the measures in it with their names and units, and the periods.","properties":{"from":{"description":"The start of the range, as resolved.","format":"date-time","type":"string"},"measures":{"description":"Every measure the answer carries, at any depth, in the order asked.","items":{"$ref":"#/components/schemas/MetricMeasure"},"type":"array"},"periods":{"description":"The periods every node's `timeseries` is cut into, in order — with no interval, the one period the whole range is.","items":{"$ref":"#/components/schemas/MetricPeriod"},"type":"array"},"to":{"description":"The end of the range, as resolved. Exclusive.","format":"date-time","type":"string"}},"required":["from","to","measures","periods"],"type":"object"},"MetricQueryValues":{"additionalProperties":false,"description":"Each measure's value for the whole range, under its key: a level as it stood at the end of the range, a flow added up over it, and a rate over the window ending at the end of the range.","properties":{"starting_mrr":{"anyOf":[{"type":"null"},{"format":"decimal","type":"string"}],"description":"`starting_mrr` is `mrr` as it stood at the start of each window. A decimal as text, or null where there is no answer."},"starting_arr":{"anyOf":[{"type":"null"},{"format":"decimal","type":"string"}],"description":"`starting_arr` is `arr` as it stood at the start of each window. A decimal as text, or null where there is no answer."},"churned_mrr":{"anyOf":[{"type":"null"},{"format":"decimal","type":"string"}],"description":"`churned_mrr` is the MRR lost in each window to customers who stopped paying, as a positive amount, including those who started inside it. `starting_mrr_lost` counts only those paying at its start. A decimal as text, or null where there is no answer."},"starting_mrr_lost":{"anyOf":[{"type":"null"},{"format":"decimal","type":"string"}],"description":"`starting_mrr_lost` is how much less per month the customers paying at the start of each window pay at its end: churn and contraction. A decimal as text, or null where there is no answer."},"customer_lifespan":{"anyOf":[{"type":"null"},{"format":"decimal","type":"string"}],"description":"`customer_lifespan` is how many months a paying customer stays on average, at each window's rate of churn. Formula: one divided by `monthly_customer_churn`. A decimal as text, or null where there is no answer."},"paying_customers":{"anyOf":[{"type":"null"},{"format":"decimal","type":"string"}],"description":"`paying_customers` is the number of customers with revenue at the end of each window. A decimal as text, or null where there is no answer."},"reactivation_arr":{"anyOf":[{"type":"null"},{"format":"decimal","type":"string"}],"description":"`reactivation_arr` is the ARR added in each window by customers who started paying again after having churned. A decimal as text, or null where there is no answer."},"arr":{"anyOf":[{"type":"null"},{"format":"decimal","type":"string"}],"description":"`arr` is the Annual Run Rate: revenue per year from every active subscription at the end of each window, after discounts, recurring and usage alike. A decimal as text, or null where there is no answer."},"net_new_paying_customers":{"anyOf":[{"type":"null"},{"format":"decimal","type":"string"}],"description":"`net_new_paying_customers` is how much `paying_customers` changed over each window, except that under a breakdown or a filter a customer who moved category is neither. Formula: `new_paying_customers` plus `reactivated_paying_customers` minus `churned_paying_customers`. A decimal as text, or null where there is no answer."},"signup_to_paid_conversion":{"anyOf":[{"type":"null"},{"format":"decimal","type":"string"}],"description":"`signup_to_paid_conversion` is the share of each window's `new_signups` who have become paying since. A recent window reads low and rises as its signups convert. Formula: `signups_converted_to_paid` divided by `new_signups`. A decimal as text, or null where there is no answer."},"mrr_growth":{"anyOf":[{"type":"null"},{"format":"decimal","type":"string"}],"description":"`mrr_growth` is how much `mrr` grew or fell over each window, as a percentage. Formula: `mrr_movement` divided by `starting_mrr`. A decimal as text, or null where there is no answer."},"lead_to_paid_conversion":{"anyOf":[{"type":"null"},{"format":"decimal","type":"string"}],"description":"`lead_to_paid_conversion` is the share of each window's `new_leads` who have become paying since. A recent window reads low and rises as its leads convert. Formula: `leads_converted_to_paid` divided by `new_leads`. A decimal as text, or null where there is no answer."},"starting_mrr_gained":{"anyOf":[{"type":"null"},{"format":"decimal","type":"string"}],"description":"`starting_mrr_gained` is how much more per month the customers paying at the start of each window pay at its end: expansion. A decimal as text, or null where there is no answer."},"reactivation_mrr":{"anyOf":[{"type":"null"},{"format":"decimal","type":"string"}],"description":"`reactivation_mrr` is the MRR added in each window by customers who started paying again after having churned. A decimal as text, or null where there is no answer."},"starting_paying_customers_retained":{"anyOf":[{"type":"null"},{"format":"decimal","type":"string"}],"description":"`starting_paying_customers_retained` is the number of customers with revenue at both the start and the end of each window. A decimal as text, or null where there is no answer."},"contraction_arr":{"anyOf":[{"type":"null"},{"format":"decimal","type":"string"}],"description":"`contraction_arr` is the ARR lost in each window to customers who came to pay less and still pay something, as a positive amount. A decimal as text, or null where there is no answer."},"new_mrr":{"anyOf":[{"type":"null"},{"format":"decimal","type":"string"}],"description":"`new_mrr` is the MRR added in each window by customers who started paying for the first time, whether or not they still pay at its end. A decimal as text, or null where there is no answer."},"monthly_customer_churn":{"anyOf":[{"type":"null"},{"format":"decimal","type":"string"}],"description":"`monthly_customer_churn` is `customer_churn` restated as a rate per month, whatever the length of each window. A decimal as text, or null where there is no answer."},"run_rate_movers":{"anyOf":[{"type":"null"},{"format":"decimal","type":"string"}],"description":"`run_rate_movers` is the number of customers whose revenue changed over each window. A decimal as text, or null where there is no answer."},"customers":{"anyOf":[{"type":"null"},{"format":"decimal","type":"string"}],"description":"`customers` is every customer known at the end of each window, whatever their status: leads, free, trialing, paying and churned. A decimal as text, or null where there is no answer."},"signups_converted_to_paid":{"anyOf":[{"type":"null"},{"format":"decimal","type":"string"}],"description":"`signups_converted_to_paid` is the number of each window's `new_signups` who have become paying since. A recent window reads low and rises as its signups convert. A decimal as text, or null where there is no answer."},"arpa_per_year":{"anyOf":[{"type":"null"},{"format":"decimal","type":"string"}],"description":"`arpa_per_year` is the Average Revenue Per Account per year: what a paying customer pays on average at the end of each window. Formula: `arr` divided by `paying_customers`. A decimal as text, or null where there is no answer."},"gross_revenue_churn":{"anyOf":[{"type":"null"},{"format":"decimal","type":"string"}],"description":"`gross_revenue_churn` is the share of revenue lost to churn and contraction over each window. Formula: `starting_mrr_lost` divided by `starting_mrr`, taken at the exchange rate of each window's end rather than its start, as `starting_mrr_lost` is. A decimal as text, or null where there is no answer."},"net_revenue_retention":{"anyOf":[{"type":"null"},{"format":"decimal","type":"string"}],"description":"`net_revenue_retention` is the share of revenue kept over each window, expansion included; above 100% when existing customers grew. Formula: `starting_mrr_retained` divided by `starting_mrr`, taken at the exchange rate of each window's end rather than its start, as `starting_mrr_retained` is. A decimal as text, or null where there is no answer."},"exchange_rate_arr":{"anyOf":[{"type":"null"},{"format":"decimal","type":"string"}],"description":"`exchange_rate_arr` is how much `arr` changed in each window because exchange rates moved, with nobody paying differently in their own currency: positive when the currencies customers pay in strengthened against the reporting currency, and zero for a project whose customers all pay in it. A decimal as text, or null where there is no answer."},"new_paying_customers":{"anyOf":[{"type":"null"},{"format":"decimal","type":"string"}],"description":"`new_paying_customers` is the number of customers who started paying for the first time in each window, whether or not they still pay at its end. A decimal as text, or null where there is no answer."},"mrr":{"anyOf":[{"type":"null"},{"format":"decimal","type":"string"}],"description":"`mrr` is the Monthly Run Rate: revenue per month from every active subscription at the end of each window, after discounts, recurring and usage alike. A decimal as text, or null where there is no answer."},"net_new_customers":{"anyOf":[{"type":"null"},{"format":"decimal","type":"string"}],"description":"`net_new_customers` is how much `customers` changed over each window. A decimal as text, or null where there is no answer."},"arr_growth":{"anyOf":[{"type":"null"},{"format":"decimal","type":"string"}],"description":"`arr_growth` is how much `arr` grew or fell over each window, as a percentage. Formula: `arr_movement` divided by `starting_arr`. A decimal as text, or null where there is no answer."},"customer_retention":{"anyOf":[{"type":"null"},{"format":"decimal","type":"string"}],"description":"`customer_retention` is the share of paying customers kept over each window. Formula: `starting_paying_customers_retained` divided by `starting_paying_customers`. A decimal as text, or null where there is no answer."},"starting_customers":{"anyOf":[{"type":"null"},{"format":"decimal","type":"string"}],"description":"`starting_customers` is `customers` as it stood at the start of each window. A decimal as text, or null where there is no answer."},"starting_arr_gained":{"anyOf":[{"type":"null"},{"format":"decimal","type":"string"}],"description":"`starting_arr_gained` is how much more per year the customers paying at the start of each window pay at its end: expansion. A decimal as text, or null where there is no answer."},"arpa_per_month":{"anyOf":[{"type":"null"},{"format":"decimal","type":"string"}],"description":"`arpa_per_month` is the Average Revenue Per Account per month: what a paying customer pays on average at the end of each window. Formula: `mrr` divided by `paying_customers`. A decimal as text, or null where there is no answer."},"starting_paying_customers":{"anyOf":[{"type":"null"},{"format":"decimal","type":"string"}],"description":"`starting_paying_customers` is `paying_customers` as it stood at the start of each window. A decimal as text, or null where there is no answer."},"ltv":{"anyOf":[{"type":"null"},{"format":"decimal","type":"string"}],"description":"`ltv` is the Lifetime Value: what a paying customer pays over the time they stay, before any cost. Formula: `arpa_per_month` times `customer_lifespan`. A decimal as text, or null where there is no answer."},"starting_mrr_retained":{"anyOf":[{"type":"null"},{"format":"decimal","type":"string"}],"description":"`starting_mrr_retained` is what the customers paying at the start of each window pay per month at its end, expansion included, so it exceeds `starting_mrr` when they grew. A decimal as text, or null where there is no answer."},"starting_arr_retained":{"anyOf":[{"type":"null"},{"format":"decimal","type":"string"}],"description":"`starting_arr_retained` is what the customers paying at the start of each window pay per year at its end, expansion included, so it exceeds `starting_arr` when they grew. A decimal as text, or null where there is no answer."},"new_leads":{"anyOf":[{"type":"null"},{"format":"decimal","type":"string"}],"description":"`new_leads` is the number of customers first seen in each window as leads. A decimal as text, or null where there is no answer."},"contraction_mrr":{"anyOf":[{"type":"null"},{"format":"decimal","type":"string"}],"description":"`contraction_mrr` is the MRR lost in each window to customers who came to pay less and still pay something, as a positive amount. A decimal as text, or null where there is no answer."},"new_arr":{"anyOf":[{"type":"null"},{"format":"decimal","type":"string"}],"description":"`new_arr` is the ARR added in each window by customers who started paying for the first time, whether or not they still pay at its end. A decimal as text, or null where there is no answer."},"median_signup_to_paid_time":{"anyOf":[{"type":"null"},{"format":"decimal","type":"string"}],"description":"`median_signup_to_paid_time` is the median number of days from signing up to first paying, of the customers who became paying in each window. A decimal as text, or null where there is no answer."},"arr_movement":{"anyOf":[{"type":"null"},{"format":"decimal","type":"string"}],"description":"`arr_movement` is how much `arr` changed over each window: what was new, expanded and reactivated, minus what contracted and churned, plus what exchange rates moved it by. Formula: `new_arr` plus `expansion_arr` plus `reactivation_arr` minus `contraction_arr` minus `churned_arr` plus `exchange_rate_arr`, and under a filter or a breakdown, what entered the category less what left it. A decimal as text, or null where there is no answer."},"gross_revenue_retention":{"anyOf":[{"type":"null"},{"format":"decimal","type":"string"}],"description":"`gross_revenue_retention` is the share of revenue kept over each window, before any expansion. Formula: `starting_mrr` minus `starting_mrr_lost`, divided by `starting_mrr`, with `starting_mrr` taken at the exchange rate of each window's end rather than its start, as `starting_mrr_lost` is. A decimal as text, or null where there is no answer."},"starting_paying_customers_lost":{"anyOf":[{"type":"null"},{"format":"decimal","type":"string"}],"description":"`starting_paying_customers_lost` is the number of customers with revenue at the start of each window who have none at its end. A decimal as text, or null where there is no answer."},"exchange_rate_mrr":{"anyOf":[{"type":"null"},{"format":"decimal","type":"string"}],"description":"`exchange_rate_mrr` is how much `mrr` changed in each window because exchange rates moved, with nobody paying differently in their own currency: positive when the currencies customers pay in strengthened against the reporting currency, and zero for a project whose customers all pay in it. A decimal as text, or null where there is no answer."},"new_signups":{"anyOf":[{"type":"null"},{"format":"decimal","type":"string"}],"description":"`new_signups` is the number of customers who signed up in each window. A decimal as text, or null where there is no answer."},"median_lead_to_paid_time":{"anyOf":[{"type":"null"},{"format":"decimal","type":"string"}],"description":"`median_lead_to_paid_time` is the median number of days from first being seen as a lead to first paying, of the customers who became paying in each window. A decimal as text, or null where there is no answer."},"churned_paying_customers":{"anyOf":[{"type":"null"},{"format":"decimal","type":"string"}],"description":"`churned_paying_customers` is how many times customers stopped paying in each window, including those who started inside it; a customer who churned twice counts twice. `starting_paying_customers_lost` counts only those paying at its start. A decimal as text, or null where there is no answer."},"starting_arr_lost":{"anyOf":[{"type":"null"},{"format":"decimal","type":"string"}],"description":"`starting_arr_lost` is how much less per year the customers paying at the start of each window pay at its end: churn and contraction. A decimal as text, or null where there is no answer."},"customer_churn":{"anyOf":[{"type":"null"},{"format":"decimal","type":"string"}],"description":"`customer_churn` is the share of paying customers lost over each window. Formula: `starting_paying_customers_lost` divided by `starting_paying_customers`. A decimal as text, or null where there is no answer."},"net_revenue_churn":{"anyOf":[{"type":"null"},{"format":"decimal","type":"string"}],"description":"`net_revenue_churn` is the share of revenue lost over each window after expansion is counted; negative when existing customers grew. Formula: `starting_mrr_lost` minus `starting_mrr_gained`, divided by `starting_mrr`, taken at the exchange rate of each window's end rather than its start, as the other two are. A decimal as text, or null where there is no answer."},"leads_converted_to_paid":{"anyOf":[{"type":"null"},{"format":"decimal","type":"string"}],"description":"`leads_converted_to_paid` is the number of each window's `new_leads` who have become paying since. A recent window reads low and rises as its leads convert. A decimal as text, or null where there is no answer."},"reactivated_paying_customers":{"anyOf":[{"type":"null"},{"format":"decimal","type":"string"}],"description":"`reactivated_paying_customers` is how many times customers started paying again in each window after having churned, whether or not they still pay at its end; a customer who came back twice counts twice. A decimal as text, or null where there is no answer."},"expansion_arr":{"anyOf":[{"type":"null"},{"format":"decimal","type":"string"}],"description":"`expansion_arr` is the ARR added in each window by paying customers who came to pay more. A decimal as text, or null where there is no answer."},"expansion_mrr":{"anyOf":[{"type":"null"},{"format":"decimal","type":"string"}],"description":"`expansion_mrr` is the MRR added in each window by paying customers who came to pay more. A decimal as text, or null where there is no answer."},"churned_arr":{"anyOf":[{"type":"null"},{"format":"decimal","type":"string"}],"description":"`churned_arr` is the ARR lost in each window to customers who stopped paying, as a positive amount, including those who started inside it. `starting_arr_lost` counts only those paying at its start. A decimal as text, or null where there is no answer."},"mrr_movement":{"anyOf":[{"type":"null"},{"format":"decimal","type":"string"}],"description":"`mrr_movement` is how much `mrr` changed over each window: what was new, expanded and reactivated, minus what contracted and churned, plus what exchange rates moved it by. Formula: `new_mrr` plus `expansion_mrr` plus `reactivation_mrr` minus `contraction_mrr` minus `churned_mrr` plus `exchange_rate_mrr`, and under a filter or a breakdown, what entered the category less what left it. A decimal as text, or null where there is no answer."}},"type":"object"},"Customer":{"additionalProperties":false,"description":"A customer is a company or a person that pays you, or might. The record is\nresolved from what every source says about them, and carries the amounts\ncomputed from their subscriptions, such as `mrr` and `status`. The changes\nto those subscriptions are the run rate movements.\n","example":{"arr":"15000.00","churned_at":null,"country_code":"US","created_at":"2025-11-03T16:20:04Z","email":"billing@northwind.example.com","first_paying_at":"2025-12-01T10:04:00Z","first_seen_at":"2025-11-03T16:20:00Z","first_subscribed_at":"2025-11-17T10:04:00Z","id":"0199a7c2-4e1b-7f38-b9d2-5a6c8e0f1d73","identities":["stripe:cus_QX7bN2kLp4Rt9v","backend:acct_4821"],"mrr":"1250.00","name":"Northwind Labs","object":"customer","properties":{"seats":25},"properties_by_source":{"backend":{"seats":25},"stripe":{"email":"billing@northwind.example.com","name":"Northwind Labs"}},"signed_up_at":"2025-11-03T16:20:00Z","status":"paying"},"properties":{"arr":{"anyOf":[{"type":"null"},{"format":"decimal","type":"string"}],"description":"What this customer is worth a year, in the project's reporting currency,\nat today's rate: `mrr`, rounded to the cent, times twelve.\n","example":"15000.00"},"churned_at":{"anyOf":[{"type":"null"},{"format":"date-time","type":"string"}],"description":"When the current churn began — the instant `status` became `churned`.\nNull while the customer is trialing or active, and cleared when they\ncome back.\n"},"country":{"anyOf":[{"type":"null"},{"allOf":[{"$ref":"#/components/schemas/Country"}]}],"description":"The country the customer is billed in. Expandable.","example":{"code":"US","name":"United States","object":"country"}},"country_code":{"anyOf":[{"type":"null"},{"type":"string"}],"description":"The country the customer is billed in, by its ISO 3166-1 alpha-2 code —\nthe `country_code` property, resolved the same way as `name`, from the\nhighest-priority source whose value is a country's code. Expand `country`\nfor its name.\n","example":"US"},"created_at":{"description":"When Summerbase first recorded the customer.","example":"2025-11-03T16:20:04Z","format":"date-time","type":"string"},"email":{"anyOf":[{"type":"null"},{"maxLength":255,"type":"string"}],"description":"The customer's address, resolved the same way as `name`.","example":"billing@northwind.example.com"},"erasures":{"anyOf":[{"type":"null"},{"items":{"$ref":"#/components/schemas/CustomerErasure"},"type":"array"}],"description":"Every erasure of their personal data, oldest first. Expandable.","example":[{"customer_id":"0199a7c2-4e1b-7f38-b9d2-5a6c8e0f1d73","erased_properties":["customer.properties.backend.phone","customer.properties.stripe.name"],"id":"019a1f04-7c2d-7b3e-8a5f-1e2d3c4b5a69","object":"customer_erasure","requested_at":"2026-10-25T09:12:40Z"}]},"first_paying_at":{"anyOf":[{"type":"null"},{"format":"date-time","type":"string"}],"description":"The first instant this customer had revenue — when `status` first became\n`paying`, and what cohort analysis groups by. Set once and kept through\nchurn and reactivation. Which month it falls in depends on the timezone\nbeing reported in.\n","example":"2025-12-01T10:04:00Z"},"first_seen_at":{"anyOf":[{"type":"null"},{"format":"date-time","type":"string"}],"description":"The earliest instant anything was asserted about this customer, their\nsubscriptions or their items — the first thing we were told. Set once.\n","example":"2025-11-03T16:20:00Z"},"first_subscribed_at":{"anyOf":[{"type":"null"},{"format":"date-time","type":"string"}],"description":"The first instant one of this customer's subscriptions was live, free or\npaying — when `status` first became one of the two. Set once and kept\nthrough churn.\n","example":"2025-11-17T10:04:00Z"},"id":{"description":"The customer's Summerbase id. Anywhere a customer is addressed, one of its `identities` works too.","example":"0199a7c2-4e1b-7f38-b9d2-5a6c8e0f1d73","format":"uuid","type":"string"},"identities":{"anyOf":[{"type":"null"},{"items":{"type":"string"},"type":"array"}],"description":"What each source calls this customer, as `source:id`. Any entry addresses it — `stripe:cus_123`.","example":["stripe:cus_QX7bN2kLp4Rt9v","backend:acct_4821"]},"mrr":{"anyOf":[{"type":"null"},{"format":"decimal","type":"string"}],"description":"What this customer is worth a month, in the project's reporting currency,\nat today's rate, rounded to the cent.\n\nEvery kind of revenue counts, each change converted from the currency it was\nbilled in, so this amount moves when a rate does and when the project's\nreporting currency changes. The same amount a filter on `customer.mrr`\ncompares and a list sorted by `mrr` orders by.\n","example":"1250.00"},"name":{"anyOf":[{"type":"null"},{"maxLength":255,"type":"string"}],"description":"What the customer is called — the `name` property, resolved to the\nhighest-priority source that has one. Write `properties.name` to change it.\n","example":"Northwind Labs"},"object":{"description":"The object's type, always `customer`.","enum":["customer"],"example":"customer","type":"string"},"properties":{"anyOf":[{"type":"null"},{"additionalProperties":{"anyOf":[{"type":"string"},{"type":"number"},{"type":"boolean"}]},"type":"object"}],"description":"**Your** source's properties on this customer, as they stand now. What you write here is what you read back — another source's values never appear in it and can never be overwritten through it. `properties_by_source` has everyone's. When you have written a key through several of this customer's ids, its value is the most recent change any of them made.","example":{"seats":25}},"properties_by_source":{"anyOf":[{"type":"null"},{"additionalProperties":{"additionalProperties":{"anyOf":[{"type":"string"},{"type":"number"},{"type":"boolean"}]},"description":"Property keys and their current values.","type":"object"},"type":"object"}],"description":"Every source's properties on this customer, keyed by source slug — including your own. Read-only: a write only ever reaches the source it is attributed to.","example":{"backend":{"seats":25},"stripe":{"email":"billing@northwind.example.com","name":"Northwind Labs"}}},"signed_up_at":{"anyOf":[{"type":"null"},{"format":"date-time","type":"string"}],"description":"When the customer signed up for the product — the `signed_up_at`\nproperty, resolved the same way as `name`. Write\n`properties.signed_up_at` to set it; a billing provider's source writes\nthe moment it first saw the customer, and a project's own source\noutranks it. A customer who has signed up and never subscribed is\n`trialing`.\n","example":"2025-11-03T16:20:00Z"},"status":{"description":"Where the customer stands with us, derived from what is live now: `paying`,\n`free`, `churned`, `trialing` or `lead`. A subscription is live while its\nstatus is `active`; paying and free are told apart by `mrr`, so `paying`\nmeans on a paid plan, whether or not an invoice has been settled. A customer\nwith nothing live who has subscribed before is `churned`; one on a trial, or\nwho has signed up and never subscribed, is `trialing`; anyone else is a\n`lead`. A change undone inside the project's collapse window never happened\nto the status either.\n","enum":["paying","free","churned","trialing","lead"],"example":"paying","type":"string"},"subscriptions":{"anyOf":[{"type":"null"},{"items":{"$ref":"#/components/schemas/Subscription"},"type":"array"}],"description":"Everything they are subscribed to, from every source. Expandable.","example":[{"annual_amount":"15000.00","billing_amounts":[{"interval":"month","interval_count":1,"net_amount":"1250.00"}],"currency_code":"USD","current_period_ends_at":"2026-11-01T10:04:00Z","customer_id":"0199a7c2-4e1b-7f38-b9d2-5a6c8e0f1d73","id":"0199a7c2-5b3e-7a14-8c6d-2f9e4b7a1c05","identities":["stripe:sub_1QhT5uV7wX9yZ2aB"],"monthly_amount":"1250.00","object":"subscription","owner_source_id":"0198f1a3-0b7c-7e25-9f41-6a2d8c3e5b19","properties":{"account_manager":"Priya Shah"},"properties_by_source":{"backend":{"account_manager":"Priya Shah"},"stripe":{"contract_id":"C-2025-118"}},"recorded_at":"2025-11-17T10:04:03Z","started_at":"2025-11-17T10:04:00Z","status":"active","trial_ends_at":"2025-12-01T10:04:00Z"}]}},"required":["created_at","id","object","status"],"type":"object"},"MetricCustomer":{"additionalProperties":false,"description":"A metric customer is one customer behind a value of the metrics report,\nwith their share of it.\n","example":{"contribution":"250.00","customer_id":"0199a7c2-4e1b-7f38-b9d2-5a6c8e0f1d73","moved_at":"2026-06-12T15:47:09Z","mrr":"1250.00","object":"metric_customer","starting_mrr":"1000.00","status":null},"properties":{"contribution":{"anyOf":[{"type":"null"},{"format":"decimal","type":"string"}],"description":"The customer's contribution to the value, in the measure's unit: what\nthey are worth under a run rate, what they moved it by under a\nmovement, what of theirs was lost or gained under a cohort metric, the\ndays they took under a median. Null under a count, where each customer\nis one.\n","example":"250.00"},"customer":{"anyOf":[{"type":"null"},{"allOf":[{"$ref":"#/components/schemas/Customer"}]}],"description":"The customer, as a record — expand it to get them on each row. Expandable.","example":{"arr":"15000.00","country_code":"US","created_at":"2025-11-03T16:20:04Z","email":"billing@northwind.example.com","first_paying_at":"2025-12-01T10:04:00Z","first_seen_at":"2025-11-03T16:20:00Z","first_subscribed_at":"2025-11-17T10:04:00Z","id":"0199a7c2-4e1b-7f38-b9d2-5a6c8e0f1d73","identities":["stripe:cus_QX7bN2kLp4Rt9v","backend:acct_4821"],"mrr":"1250.00","name":"Northwind Labs","object":"customer","properties":{"seats":25},"properties_by_source":{"backend":{"seats":25},"stripe":{"email":"billing@northwind.example.com","name":"Northwind Labs"}},"signed_up_at":"2025-11-03T16:20:00Z","status":"paying"}},"customer_id":{"description":"Who.","example":"0199a7c2-4e1b-7f38-b9d2-5a6c8e0f1d73","format":"uuid","type":"string"},"moved_at":{"anyOf":[{"type":"null"},{"format":"date-time","type":"string"}],"description":"The last instant inside the window at which what the customer is\nworth under the cell's categories moved — under a type of movement,\na movement of that type: when they became new, expanded or churned.\nNull if it did not move, and for a metric not read from revenue.\n","example":"2026-06-12T15:47:09Z"},"mrr":{"anyOf":[{"type":"null"},{"format":"decimal","type":"string"}],"description":"What the customer was worth a month at the window's end, under the cell's\ncategories, in the reporting currency — except under a cohort metric, where\nit is what the customer is worth at the end wherever it sits, their share of\nit under the cell's categories. Null for a metric not read from revenue.\n","example":"1250.00"},"object":{"description":"The object's type, always `metric_customer`.","enum":["metric_customer"],"example":"metric_customer","type":"string"},"starting_mrr":{"anyOf":[{"type":"null"},{"format":"decimal","type":"string"}],"description":"What the customer was worth a month at the window's start, under the\ncell's categories, in the reporting currency at the end's rate. Null\nfor a metric not read from revenue.\n","example":"1000.00"},"status":{"anyOf":[{"type":"null"},{"enum":["paying","free","churned","trialing","lead"],"type":"string"}],"description":"Where the customer stood at the instant a count of customers is read\nat. Null for any other metric.\n"}},"required":["customer_id","object"],"type":"object"},"MetricQuerySnapshot":{"additionalProperties":false,"description":"Each measure's value as it stood at the end of one day, under its key.","properties":{"starting_mrr":{"anyOf":[{"type":"null"},{"format":"decimal","type":"string"}],"description":"`starting_mrr` is `mrr` as it stood at the start of each window. A decimal as text, or null where there is no answer."},"starting_arr":{"anyOf":[{"type":"null"},{"format":"decimal","type":"string"}],"description":"`starting_arr` is `arr` as it stood at the start of each window. A decimal as text, or null where there is no answer."},"churned_mrr":{"anyOf":[{"type":"null"},{"format":"decimal","type":"string"}],"description":"`churned_mrr` is the MRR lost in each window to customers who stopped paying, as a positive amount, including those who started inside it. `starting_mrr_lost` counts only those paying at its start. A decimal as text, or null where there is no answer."},"starting_mrr_lost":{"anyOf":[{"type":"null"},{"format":"decimal","type":"string"}],"description":"`starting_mrr_lost` is how much less per month the customers paying at the start of each window pay at its end: churn and contraction. A decimal as text, or null where there is no answer."},"customer_lifespan":{"anyOf":[{"type":"null"},{"format":"decimal","type":"string"}],"description":"`customer_lifespan` is how many months a paying customer stays on average, at each window's rate of churn. Formula: one divided by `monthly_customer_churn`. A decimal as text, or null where there is no answer."},"paying_customers":{"anyOf":[{"type":"null"},{"format":"decimal","type":"string"}],"description":"`paying_customers` is the number of customers with revenue at the end of each window. A decimal as text, or null where there is no answer."},"reactivation_arr":{"anyOf":[{"type":"null"},{"format":"decimal","type":"string"}],"description":"`reactivation_arr` is the ARR added in each window by customers who started paying again after having churned. A decimal as text, or null where there is no answer."},"arr":{"anyOf":[{"type":"null"},{"format":"decimal","type":"string"}],"description":"`arr` is the Annual Run Rate: revenue per year from every active subscription at the end of each window, after discounts, recurring and usage alike. A decimal as text, or null where there is no answer."},"net_new_paying_customers":{"anyOf":[{"type":"null"},{"format":"decimal","type":"string"}],"description":"`net_new_paying_customers` is how much `paying_customers` changed over each window, except that under a breakdown or a filter a customer who moved category is neither. Formula: `new_paying_customers` plus `reactivated_paying_customers` minus `churned_paying_customers`. A decimal as text, or null where there is no answer."},"signup_to_paid_conversion":{"anyOf":[{"type":"null"},{"format":"decimal","type":"string"}],"description":"`signup_to_paid_conversion` is the share of each window's `new_signups` who have become paying since. A recent window reads low and rises as its signups convert. Formula: `signups_converted_to_paid` divided by `new_signups`. A decimal as text, or null where there is no answer."},"mrr_growth":{"anyOf":[{"type":"null"},{"format":"decimal","type":"string"}],"description":"`mrr_growth` is how much `mrr` grew or fell over each window, as a percentage. Formula: `mrr_movement` divided by `starting_mrr`. A decimal as text, or null where there is no answer."},"lead_to_paid_conversion":{"anyOf":[{"type":"null"},{"format":"decimal","type":"string"}],"description":"`lead_to_paid_conversion` is the share of each window's `new_leads` who have become paying since. A recent window reads low and rises as its leads convert. Formula: `leads_converted_to_paid` divided by `new_leads`. A decimal as text, or null where there is no answer."},"starting_mrr_gained":{"anyOf":[{"type":"null"},{"format":"decimal","type":"string"}],"description":"`starting_mrr_gained` is how much more per month the customers paying at the start of each window pay at its end: expansion. A decimal as text, or null where there is no answer."},"reactivation_mrr":{"anyOf":[{"type":"null"},{"format":"decimal","type":"string"}],"description":"`reactivation_mrr` is the MRR added in each window by customers who started paying again after having churned. A decimal as text, or null where there is no answer."},"starting_paying_customers_retained":{"anyOf":[{"type":"null"},{"format":"decimal","type":"string"}],"description":"`starting_paying_customers_retained` is the number of customers with revenue at both the start and the end of each window. A decimal as text, or null where there is no answer."},"contraction_arr":{"anyOf":[{"type":"null"},{"format":"decimal","type":"string"}],"description":"`contraction_arr` is the ARR lost in each window to customers who came to pay less and still pay something, as a positive amount. A decimal as text, or null where there is no answer."},"new_mrr":{"anyOf":[{"type":"null"},{"format":"decimal","type":"string"}],"description":"`new_mrr` is the MRR added in each window by customers who started paying for the first time, whether or not they still pay at its end. A decimal as text, or null where there is no answer."},"monthly_customer_churn":{"anyOf":[{"type":"null"},{"format":"decimal","type":"string"}],"description":"`monthly_customer_churn` is `customer_churn` restated as a rate per month, whatever the length of each window. A decimal as text, or null where there is no answer."},"at":{"description":"The end of the day the snapshot reads at. Exclusive.","format":"date-time","type":"string"},"run_rate_movers":{"anyOf":[{"type":"null"},{"format":"decimal","type":"string"}],"description":"`run_rate_movers` is the number of customers whose revenue changed over each window. A decimal as text, or null where there is no answer."},"customers":{"anyOf":[{"type":"null"},{"format":"decimal","type":"string"}],"description":"`customers` is every customer known at the end of each window, whatever their status: leads, free, trialing, paying and churned. A decimal as text, or null where there is no answer."},"signups_converted_to_paid":{"anyOf":[{"type":"null"},{"format":"decimal","type":"string"}],"description":"`signups_converted_to_paid` is the number of each window's `new_signups` who have become paying since. A recent window reads low and rises as its signups convert. A decimal as text, or null where there is no answer."},"arpa_per_year":{"anyOf":[{"type":"null"},{"format":"decimal","type":"string"}],"description":"`arpa_per_year` is the Average Revenue Per Account per year: what a paying customer pays on average at the end of each window. Formula: `arr` divided by `paying_customers`. A decimal as text, or null where there is no answer."},"gross_revenue_churn":{"anyOf":[{"type":"null"},{"format":"decimal","type":"string"}],"description":"`gross_revenue_churn` is the share of revenue lost to churn and contraction over each window. Formula: `starting_mrr_lost` divided by `starting_mrr`, taken at the exchange rate of each window's end rather than its start, as `starting_mrr_lost` is. A decimal as text, or null where there is no answer."},"net_revenue_retention":{"anyOf":[{"type":"null"},{"format":"decimal","type":"string"}],"description":"`net_revenue_retention` is the share of revenue kept over each window, expansion included; above 100% when existing customers grew. Formula: `starting_mrr_retained` divided by `starting_mrr`, taken at the exchange rate of each window's end rather than its start, as `starting_mrr_retained` is. A decimal as text, or null where there is no answer."},"exchange_rate_arr":{"anyOf":[{"type":"null"},{"format":"decimal","type":"string"}],"description":"`exchange_rate_arr` is how much `arr` changed in each window because exchange rates moved, with nobody paying differently in their own currency: positive when the currencies customers pay in strengthened against the reporting currency, and zero for a project whose customers all pay in it. A decimal as text, or null where there is no answer."},"new_paying_customers":{"anyOf":[{"type":"null"},{"format":"decimal","type":"string"}],"description":"`new_paying_customers` is the number of customers who started paying for the first time in each window, whether or not they still pay at its end. A decimal as text, or null where there is no answer."},"mrr":{"anyOf":[{"type":"null"},{"format":"decimal","type":"string"}],"description":"`mrr` is the Monthly Run Rate: revenue per month from every active subscription at the end of each window, after discounts, recurring and usage alike. A decimal as text, or null where there is no answer."},"net_new_customers":{"anyOf":[{"type":"null"},{"format":"decimal","type":"string"}],"description":"`net_new_customers` is how much `customers` changed over each window. A decimal as text, or null where there is no answer."},"arr_growth":{"anyOf":[{"type":"null"},{"format":"decimal","type":"string"}],"description":"`arr_growth` is how much `arr` grew or fell over each window, as a percentage. Formula: `arr_movement` divided by `starting_arr`. A decimal as text, or null where there is no answer."},"customer_retention":{"anyOf":[{"type":"null"},{"format":"decimal","type":"string"}],"description":"`customer_retention` is the share of paying customers kept over each window. Formula: `starting_paying_customers_retained` divided by `starting_paying_customers`. A decimal as text, or null where there is no answer."},"starting_customers":{"anyOf":[{"type":"null"},{"format":"decimal","type":"string"}],"description":"`starting_customers` is `customers` as it stood at the start of each window. A decimal as text, or null where there is no answer."},"starting_arr_gained":{"anyOf":[{"type":"null"},{"format":"decimal","type":"string"}],"description":"`starting_arr_gained` is how much more per year the customers paying at the start of each window pay at its end: expansion. A decimal as text, or null where there is no answer."},"arpa_per_month":{"anyOf":[{"type":"null"},{"format":"decimal","type":"string"}],"description":"`arpa_per_month` is the Average Revenue Per Account per month: what a paying customer pays on average at the end of each window. Formula: `mrr` divided by `paying_customers`. A decimal as text, or null where there is no answer."},"starting_paying_customers":{"anyOf":[{"type":"null"},{"format":"decimal","type":"string"}],"description":"`starting_paying_customers` is `paying_customers` as it stood at the start of each window. A decimal as text, or null where there is no answer."},"ltv":{"anyOf":[{"type":"null"},{"format":"decimal","type":"string"}],"description":"`ltv` is the Lifetime Value: what a paying customer pays over the time they stay, before any cost. Formula: `arpa_per_month` times `customer_lifespan`. A decimal as text, or null where there is no answer."},"starting_mrr_retained":{"anyOf":[{"type":"null"},{"format":"decimal","type":"string"}],"description":"`starting_mrr_retained` is what the customers paying at the start of each window pay per month at its end, expansion included, so it exceeds `starting_mrr` when they grew. A decimal as text, or null where there is no answer."},"starting_arr_retained":{"anyOf":[{"type":"null"},{"format":"decimal","type":"string"}],"description":"`starting_arr_retained` is what the customers paying at the start of each window pay per year at its end, expansion included, so it exceeds `starting_arr` when they grew. A decimal as text, or null where there is no answer."},"new_leads":{"anyOf":[{"type":"null"},{"format":"decimal","type":"string"}],"description":"`new_leads` is the number of customers first seen in each window as leads. A decimal as text, or null where there is no answer."},"contraction_mrr":{"anyOf":[{"type":"null"},{"format":"decimal","type":"string"}],"description":"`contraction_mrr` is the MRR lost in each window to customers who came to pay less and still pay something, as a positive amount. A decimal as text, or null where there is no answer."},"new_arr":{"anyOf":[{"type":"null"},{"format":"decimal","type":"string"}],"description":"`new_arr` is the ARR added in each window by customers who started paying for the first time, whether or not they still pay at its end. A decimal as text, or null where there is no answer."},"median_signup_to_paid_time":{"anyOf":[{"type":"null"},{"format":"decimal","type":"string"}],"description":"`median_signup_to_paid_time` is the median number of days from signing up to first paying, of the customers who became paying in each window. A decimal as text, or null where there is no answer."},"arr_movement":{"anyOf":[{"type":"null"},{"format":"decimal","type":"string"}],"description":"`arr_movement` is how much `arr` changed over each window: what was new, expanded and reactivated, minus what contracted and churned, plus what exchange rates moved it by. Formula: `new_arr` plus `expansion_arr` plus `reactivation_arr` minus `contraction_arr` minus `churned_arr` plus `exchange_rate_arr`, and under a filter or a breakdown, what entered the category less what left it. A decimal as text, or null where there is no answer."},"gross_revenue_retention":{"anyOf":[{"type":"null"},{"format":"decimal","type":"string"}],"description":"`gross_revenue_retention` is the share of revenue kept over each window, before any expansion. Formula: `starting_mrr` minus `starting_mrr_lost`, divided by `starting_mrr`, with `starting_mrr` taken at the exchange rate of each window's end rather than its start, as `starting_mrr_lost` is. A decimal as text, or null where there is no answer."},"starting_paying_customers_lost":{"anyOf":[{"type":"null"},{"format":"decimal","type":"string"}],"description":"`starting_paying_customers_lost` is the number of customers with revenue at the start of each window who have none at its end. A decimal as text, or null where there is no answer."},"exchange_rate_mrr":{"anyOf":[{"type":"null"},{"format":"decimal","type":"string"}],"description":"`exchange_rate_mrr` is how much `mrr` changed in each window because exchange rates moved, with nobody paying differently in their own currency: positive when the currencies customers pay in strengthened against the reporting currency, and zero for a project whose customers all pay in it. A decimal as text, or null where there is no answer."},"new_signups":{"anyOf":[{"type":"null"},{"format":"decimal","type":"string"}],"description":"`new_signups` is the number of customers who signed up in each window. A decimal as text, or null where there is no answer."},"median_lead_to_paid_time":{"anyOf":[{"type":"null"},{"format":"decimal","type":"string"}],"description":"`median_lead_to_paid_time` is the median number of days from first being seen as a lead to first paying, of the customers who became paying in each window. A decimal as text, or null where there is no answer."},"churned_paying_customers":{"anyOf":[{"type":"null"},{"format":"decimal","type":"string"}],"description":"`churned_paying_customers` is how many times customers stopped paying in each window, including those who started inside it; a customer who churned twice counts twice. `starting_paying_customers_lost` counts only those paying at its start. A decimal as text, or null where there is no answer."},"starting_arr_lost":{"anyOf":[{"type":"null"},{"format":"decimal","type":"string"}],"description":"`starting_arr_lost` is how much less per year the customers paying at the start of each window pay at its end: churn and contraction. A decimal as text, or null where there is no answer."},"customer_churn":{"anyOf":[{"type":"null"},{"format":"decimal","type":"string"}],"description":"`customer_churn` is the share of paying customers lost over each window. Formula: `starting_paying_customers_lost` divided by `starting_paying_customers`. A decimal as text, or null where there is no answer."},"net_revenue_churn":{"anyOf":[{"type":"null"},{"format":"decimal","type":"string"}],"description":"`net_revenue_churn` is the share of revenue lost over each window after expansion is counted; negative when existing customers grew. Formula: `starting_mrr_lost` minus `starting_mrr_gained`, divided by `starting_mrr`, taken at the exchange rate of each window's end rather than its start, as the other two are. A decimal as text, or null where there is no answer."},"leads_converted_to_paid":{"anyOf":[{"type":"null"},{"format":"decimal","type":"string"}],"description":"`leads_converted_to_paid` is the number of each window's `new_leads` who have become paying since. A recent window reads low and rises as its leads convert. A decimal as text, or null where there is no answer."},"reactivated_paying_customers":{"anyOf":[{"type":"null"},{"format":"decimal","type":"string"}],"description":"`reactivated_paying_customers` is how many times customers started paying again in each window after having churned, whether or not they still pay at its end; a customer who came back twice counts twice. A decimal as text, or null where there is no answer."},"expansion_arr":{"anyOf":[{"type":"null"},{"format":"decimal","type":"string"}],"description":"`expansion_arr` is the ARR added in each window by paying customers who came to pay more. A decimal as text, or null where there is no answer."},"label":{"description":"`Now`, `30 days ago`, or the day.","type":"string"},"expansion_mrr":{"anyOf":[{"type":"null"},{"format":"decimal","type":"string"}],"description":"`expansion_mrr` is the MRR added in each window by paying customers who came to pay more. A decimal as text, or null where there is no answer."},"churned_arr":{"anyOf":[{"type":"null"},{"format":"decimal","type":"string"}],"description":"`churned_arr` is the ARR lost in each window to customers who stopped paying, as a positive amount, including those who started inside it. `starting_arr_lost` counts only those paying at its start. A decimal as text, or null where there is no answer."},"mrr_movement":{"anyOf":[{"type":"null"},{"format":"decimal","type":"string"}],"description":"`mrr_movement` is how much `mrr` changed over each window: what was new, expanded and reactivated, minus what contracted and churned, plus what exchange rates moved it by. Formula: `new_mrr` plus `expansion_mrr` plus `reactivation_mrr` minus `contraction_mrr` minus `churned_mrr` plus `exchange_rate_mrr`, and under a filter or a breakdown, what entered the category less what left it. A decimal as text, or null where there is no answer."}},"required":["label","at"],"type":"object"},"PropertyInput":{"additionalProperties":false,"properties":{"operation":{"anyOf":[{"type":"null"},{"enum":["set","increment","set_once"],"type":"string"}],"description":"What to do with the value. Defaults to setting it."},"type":{"anyOf":[{"type":"null"},{"enum":["string","number","boolean","datetime"],"type":"string"}],"description":"What this value is. Given here, it is not inferred; omitted, the key's\nexisting definition decides, and a new key takes the type its first value\nlooks like.\n"},"value":{"anyOf":[{"type":"null"},{"type":"string"}],"description":"The value, as a string. Parsed into the key's type — which `type` states\noutright, or which the key's existing definition decides. Null clears the\nkey.\n"}},"type":"object"},"Subscription":{"additionalProperties":false,"description":"A subscription is what a customer is subscribed to, in the one source it\nlives in. It is made of items that carry the prices, and its\n`monthly_amount` is what they are worth a month; a customer's `mrr` is the\nsum over their subscriptions.\n","example":{"annual_amount":"15000.00","billing_amounts":[{"interval":"month","interval_count":1,"net_amount":"1250.00"}],"cancels_at":null,"currency_code":"USD","current_period_ends_at":"2026-11-01T10:04:00Z","customer_id":"0199a7c2-4e1b-7f38-b9d2-5a6c8e0f1d73","ended_at":null,"id":"0199a7c2-5b3e-7a14-8c6d-2f9e4b7a1c05","identities":["stripe:sub_1QhT5uV7wX9yZ2aB"],"monthly_amount":"1250.00","object":"subscription","owner_source_id":"0198f1a3-0b7c-7e25-9f41-6a2d8c3e5b19","properties":{"account_manager":"Priya Shah"},"properties_by_source":{"backend":{"account_manager":"Priya Shah"},"stripe":{"contract_id":"C-2025-118"}},"recorded_at":"2025-11-17T10:04:03Z","started_at":"2025-11-17T10:04:00Z","status":"active","trial_ends_at":"2025-12-01T10:04:00Z"},"properties":{"annual_amount":{"anyOf":[{"type":"null"},{"format":"decimal","type":"string"}],"description":"What this subscription is worth a year, in `currency_code`:\n`monthly_amount` times twelve.\n","example":"15000.00"},"billing_amounts":{"anyOf":[{"type":"null"},{"items":{"$ref":"#/components/schemas/SubscriptionBillingAmount"},"type":"array"}],"description":"What this subscription comes to each time it bills: the items still on\nit, their `net_amount` summed per billing interval, longest interval\nfirst. One entry for nearly every subscription; one per interval for a\nsubscription whose items bill on different ones, such as a yearly fee\nbeside a monthly one. Its price whatever its status — a trial still has\none. `monthly_amount` is what it is worth on one footing.\n","example":[{"interval":"month","interval_count":1,"net_amount":"1250.00"}]},"cancels_at":{"anyOf":[{"type":"null"},{"format":"date-time","type":"string"}],"description":"A cancellation already booked. It is still in service until then, and\nthe source says so with `status: canceled` when it stops.\n"},"currency":{"anyOf":[{"type":"null"},{"allOf":[{"$ref":"#/components/schemas/Currency"}]}],"description":"The currency it bills in. Expandable.","example":{"code":"USD","name":"US dollar","object":"currency"}},"currency_code":{"description":"The currency it bills in, by its ISO 4217 code.","example":"USD","maxLength":3,"minLength":3,"type":"string"},"current_period_ends_at":{"anyOf":[{"type":"null"},{"format":"date-time","type":"string"}],"description":"When the current billing period ends. The next charge is due then,\nunless a cancellation is booked for it.\n","example":"2026-11-01T10:04:00Z"},"customer":{"anyOf":[{"type":"null"},{"allOf":[{"$ref":"#/components/schemas/Customer"}]}],"description":"The customer who is subscribed. Expandable.","example":{"arr":"15000.00","country_code":"US","created_at":"2025-11-03T16:20:04Z","email":"billing@northwind.example.com","first_paying_at":"2025-12-01T10:04:00Z","first_seen_at":"2025-11-03T16:20:00Z","first_subscribed_at":"2025-11-17T10:04:00Z","id":"0199a7c2-4e1b-7f38-b9d2-5a6c8e0f1d73","identities":["stripe:cus_QX7bN2kLp4Rt9v","backend:acct_4821"],"mrr":"1250.00","name":"Northwind Labs","object":"customer","properties":{"seats":25},"properties_by_source":{"backend":{"seats":25},"stripe":{"email":"billing@northwind.example.com","name":"Northwind Labs"}},"signed_up_at":"2025-11-03T16:20:00Z","status":"paying"}},"customer_id":{"description":"The customer who is subscribed.","example":"0199a7c2-4e1b-7f38-b9d2-5a6c8e0f1d73","format":"uuid","type":"string"},"ended_at":{"anyOf":[{"type":"null"},{"format":"date-time","type":"string"}],"description":"When service stopped, beside `status: canceled`. Clear it, with `status`\nback to `active`, to resume a subscription; the same id may be brought\nback as often as the source does so.\n"},"id":{"description":"The subscription's Summerbase id. Anywhere a subscription is addressed, one of its `identities` works too.","example":"0199a7c2-5b3e-7a14-8c6d-2f9e4b7a1c05","format":"uuid","type":"string"},"identities":{"anyOf":[{"type":"null"},{"items":{"type":"string"},"type":"array"}],"description":"What each source calls this subscription, as `source:id`. Any entry addresses it — `stripe:cus_123`.","example":["stripe:sub_1QhT5uV7wX9yZ2aB"]},"items":{"anyOf":[{"type":"null"},{"items":{"$ref":"#/components/schemas/SubscriptionItem"},"type":"array"}],"description":"The items it is made of, and where the money is. Expandable.","example":[{"created_at":"2025-11-17T10:04:03Z","deleted":false,"discount_amount":"0","id":"0199a7c2-5b41-7c62-9d3e-6a1f8b2c4d97","identities":["stripe:si_QhT6vW8xY0zA3b"],"interval":"month","interval_count":1,"net_amount":"1250.00","object":"subscription_item","plan_id":"0198f1a4-1d2e-7a40-9b31-5c6d7e8f9a0b","plan_name":"Growth","price_id":"0198f1a4-2e5f-7b80-a1c2-3d4e5f6a7b8c","price_name":"Growth monthly","pricing_model":"fixed","properties":{"seat_type":"standard"},"properties_by_source":{"backend":{"seat_type":"standard"}},"quantity":"25","subscription_id":"0199a7c2-5b3e-7a14-8c6d-2f9e4b7a1c05","unit_amount":"50.00"}]},"monthly_amount":{"description":"What this subscription is worth a month, in `currency_code`, after the\nitems' discounts — **derived, not reported.** The sum of what its items\nare worth; zero while it is on trial, paused or ended, and zero on a\nfree plan. A customer's `mrr` is these summed across their subscriptions, in\nthe reporting currency.\n","example":"1250.00","format":"decimal","type":"string"},"object":{"description":"The object's type, always `subscription`.","enum":["subscription"],"example":"subscription","type":"string"},"owner_source":{"anyOf":[{"type":"null"},{"allOf":[{"$ref":"#/components/schemas/Integration"}]}],"description":"The source this subscription lives in — the only one that may write its\nown fields. Any source may attach properties and identities to it.\n Expandable.","example":{"connected_at":"2025-09-08T14:10:05Z","created_at":"2025-09-08T14:09:47Z","current_import_id":"019a0c0f-6b1d-7e3a-8c2f-4d5e6f7a8b9c","id":"0198f1a3-0b7c-7e25-9f41-6a2d8c3e5b19","imported_at":"2025-09-08T14:26:51Z","is_source":true,"name":"Stripe","object":"integration","priority":2,"project_id":"0198f1a2-6c52-7b18-8e3f-2a9c4d6b1e07","provider_account_id":"acct_1QfR3sT8uVw2XyZa","provider_code":"stripe","sandbox":false,"slug":"stripe","webhook_installed_at":"2025-09-08T14:10:06Z","webhook_url":"https://api.summerbase.com/webhooks/integrations/0198f1a3-0b7c-7e25-9f41-6a2d8c3e5b19?c=us1"}},"owner_source_id":{"description":"The source this subscription lives in — the only one that may write its\nown fields. Any source may attach properties and identities to it.\n","example":"0198f1a3-0b7c-7e25-9f41-6a2d8c3e5b19","format":"uuid","type":"string"},"properties":{"anyOf":[{"type":"null"},{"additionalProperties":{"anyOf":[{"type":"string"},{"type":"number"},{"type":"boolean"}]},"type":"object"}],"description":"**Your** source's properties on this subscription, as they stand now. What you write here is what you read back — another source's values never appear in it and can never be overwritten through it. `properties_by_source` has everyone's. When you have written a key through several of this subscription's ids, its value is the most recent change any of them made.","example":{"account_manager":"Priya Shah"}},"properties_by_source":{"anyOf":[{"type":"null"},{"additionalProperties":{"additionalProperties":{"anyOf":[{"type":"string"},{"type":"number"},{"type":"boolean"}]},"description":"Property keys and their current values.","type":"object"},"type":"object"}],"description":"Every source's properties on this subscription, keyed by source slug — including your own. Read-only: a write only ever reaches the source it is attributed to.","example":{"backend":{"account_manager":"Priya Shah"},"stripe":{"contract_id":"C-2025-118"}}},"recorded_at":{"description":"When Summerbase first recorded it. Never set by a caller.","example":"2025-11-17T10:04:03Z","format":"date-time","type":"string"},"started_at":{"description":"When the subscription began — **derived, not reported.**\n\nIt is the earliest instant anything was asserted about the subscription or\nits items. A source cannot say it began earlier than it described it: send\nthe earliest write with `effective_at` at the real start instead, which is\nwhat an import of history does anyway.\n\nIt moves earlier when a later write describes an earlier moment.\n","example":"2025-11-17T10:04:00Z","format":"date-time","type":"string"},"status":{"description":"Where it is in its life. Not whether it has been paid for.","enum":["pending","trialing","active","paused","canceled"],"example":"active","type":"string"},"trial_ends_at":{"anyOf":[{"type":"null"},{"format":"date-time","type":"string"}],"description":"When the trial ends, or ended. A subscription that trials starts\ntrialing, so when it began is `started_at` and its status history.\n","example":"2025-12-01T10:04:00Z"}},"required":["currency_code","customer_id","id","monthly_amount","object","owner_source_id","recorded_at","started_at","status"],"type":"object"},"Plan":{"additionalProperties":false,"description":"A plan is one thing you sell — Pro, Team — and the prices it is billed as:\nmonthly and annual, one per currency, and the prices of the same plan in\nevery billing system you have used. Metrics can be broken down and filtered\nby plan. A plan is created with the first price that names it, or ahead of\ntime, and plans can be renamed and merged without touching the\nsubscriptions on them.\n","example":{"archived":false,"created_at":"2025-09-08T14:12:30Z","id":"0198f1a4-1d2e-7a40-9b31-5c6d7e8f9a0b","identities":["stripe:prod_QhR2sT4uV6wX8y"],"name":"Growth","object":"plan","price_count":2,"properties":{"tier":"mid"},"properties_by_source":{"backend":{"tier":"mid"}}},"properties":{"archived":{"description":"Whether the plan has stopped being offered — the `archived` property,\nresolved to the highest-priority source that says one; Stripe says it\nof a product it has archived. Write `properties.archived` to change it.\nExisting subscriptions keep the plan: what somebody paid for last year\ndoes not stop being true when you withdraw it.\n","example":false,"type":"boolean"},"created_at":{"description":"When the plan was first seen.","example":"2025-09-08T14:12:30Z","format":"date-time","type":"string"},"id":{"description":"The plan's Summerbase id. Anywhere a plan is addressed, one of its `identities` works too.","example":"0198f1a4-1d2e-7a40-9b31-5c6d7e8f9a0b","format":"uuid","type":"string"},"identities":{"anyOf":[{"type":"null"},{"items":{"type":"string"},"type":"array"}],"description":"What each source calls this plan, as `source:id`. Any entry addresses it — `stripe:cus_123`.","example":["stripe:prod_QhR2sT4uV6wX8y"]},"name":{"anyOf":[{"type":"null"},{"maxLength":255,"type":"string"}],"description":"What the plan is called in a filter, a legend or a breakdown — the\n`name` property, resolved to the highest-priority source that has one.\nWrite `properties.name` to change it. Null when no source names it.\n","example":"Growth"},"object":{"description":"The object's type, always `plan`.","enum":["plan"],"example":"plan","type":"string"},"price_count":{"description":"How many prices are on this plan.","example":2,"type":"integer"},"prices":{"anyOf":[{"type":"null"},{"items":{"$ref":"#/components/schemas/Price"},"type":"array"}],"description":"The prices on this plan, in every source. Expandable.","example":[{"archived":false,"created_at":"2025-09-08T14:12:30Z","currency_code":"USD","id":"0198f1a4-2e5f-7b80-a1c2-3d4e5f6a7b8c","identities":["stripe:price_1PqR8sT2uV4wX6yZ"],"interval":"month","interval_count":1,"name":"Growth monthly","object":"price","owner_source_id":"0198f1a3-0b7c-7e25-9f41-6a2d8c3e5b19","plan_id":"0198f1a4-1d2e-7a40-9b31-5c6d7e8f9a0b","pricing_model":"fixed","properties":{"tier":"mid"},"properties_by_source":{"backend":{"tier":"mid"}},"tax_behavior":"exclusive","unit_amount":"50.00"}]},"properties":{"anyOf":[{"type":"null"},{"additionalProperties":{"anyOf":[{"type":"string"},{"type":"number"},{"type":"boolean"}]},"type":"object"}],"description":"**Your** source's properties on this plan, as they stand now. What you write here is what you read back — another source's values never appear in it and can never be overwritten through it. `properties_by_source` has everyone's. When you have written a key through several of this plan's ids, its value is the most recent change any of them made.","example":{"tier":"mid"}},"properties_by_source":{"anyOf":[{"type":"null"},{"additionalProperties":{"additionalProperties":{"anyOf":[{"type":"string"},{"type":"number"},{"type":"boolean"}]},"description":"Property keys and their current values.","type":"object"},"type":"object"}],"description":"Every source's properties on this plan, keyed by source slug — including your own. Read-only: a write only ever reaches the source it is attributed to.","example":{"backend":{"tier":"mid"}}}},"required":["archived","created_at","id","object","price_count"],"type":"object"},"UpdateSubscriptionItemResult":{"additionalProperties":false,"description":"A subscription item is one price on a subscription: a price, a quantity and a\nunit amount, billed every interval. Its plan is its price's. A subscription's amounts are summed from\nits items. Items are usually written with their subscription, as its\n`items`, and can also be changed one at a time.\n","example":{"created_at":"2025-11-17T10:04:03Z","deleted":false,"discount_amount":"0","discount_ends_at":null,"id":"0199a7c2-5b41-7c62-9d3e-6a1f8b2c4d97","identities":["stripe:si_QhT6vW8xY0zA3b"],"interval":"month","interval_count":1,"net_amount":"1250.00","object":"subscription_item","plan_id":"0198f1a4-1d2e-7a40-9b31-5c6d7e8f9a0b","plan_name":"Growth","price_id":"0198f1a4-2e5f-7b80-a1c2-3d4e5f6a7b8c","price_name":"Growth monthly","pricing_model":"fixed","properties":{"seat_type":"standard"},"properties_by_source":{"backend":{"seat_type":"standard"}},"quantity":"25","subscription_id":"0199a7c2-5b3e-7a14-8c6d-2f9e4b7a1c05","unit_amount":"50.00"},"properties":{"created_at":{"description":"When this item was first recorded.","example":"2025-11-17T10:04:03Z","format":"date-time","type":"string"},"deleted":{"description":"Whether the item has come off the subscription. **When** it did is the\n`effective_at` of the write that set this.\n","example":false,"type":"boolean"},"discount_amount":{"description":"What comes off this item's invoice, in the subscription's currency,\nafter every discount the source applies to it has been worked out:\ncoupons at any level, stacked in the source's order, restricted to\nthe products they cover. The item is worth `unit_amount × quantity`\nless this, each interval. Zero is no discount.\n","example":"0","format":"decimal","type":"string"},"discount_ends_at":{"anyOf":[{"type":"null"},{"format":"date-time","type":"string"}],"description":"When the discount, or the earliest part of it, stops applying, if the\nsource knows. Information for a reader: the discount comes off when the\nsource writes a smaller `discount_amount`, not when this passes.\n"},"id":{"description":"The item's Summerbase id. Anywhere an item is addressed, one of its `identities` works too.","example":"0199a7c2-5b41-7c62-9d3e-6a1f8b2c4d97","format":"uuid","type":"string"},"identities":{"anyOf":[{"type":"null"},{"items":{"type":"string"},"type":"array"}],"description":"What each source calls this subscription item, as `source:id`. Any entry addresses it — `stripe:cus_123`.","example":["stripe:si_QhT6vW8xY0zA3b"]},"interval":{"description":"The unit this item bills in.","enum":["day","week","month","year"],"example":"month","type":"string"},"interval_count":{"description":"How many intervals between charges.","example":1,"minimum":1,"type":"integer"},"net_amount":{"anyOf":[{"type":"null"},{"format":"decimal","type":"string"}],"description":"What this item comes to each time it is billed, in its subscription's\ncurrency: `unit_amount` times `quantity`, less `discount_amount`.\n","example":"1250.00"},"object":{"description":"The object's type, always `subscription_item`.","enum":["subscription_item"],"example":"subscription_item","type":"string"},"plan_id":{"anyOf":[{"type":"null"},{"format":"uuid","type":"string"}],"description":"The plan this item is on: its price's.","example":"0198f1a4-1d2e-7a40-9b31-5c6d7e8f9a0b"},"plan_name":{"anyOf":[{"type":"null"},{"type":"string"}],"description":"What the plan this item is on is called: its price's.","example":"Growth"},"price":{"anyOf":[{"type":"null"},{"allOf":[{"$ref":"#/components/schemas/Price"}]}],"description":"The price this item is on, when a source named one. Expandable.","example":{"archived":false,"created_at":"2025-09-08T14:12:30Z","currency_code":"USD","id":"0198f1a4-2e5f-7b80-a1c2-3d4e5f6a7b8c","identities":["stripe:price_1PqR8sT2uV4wX6yZ"],"interval":"month","interval_count":1,"name":"Growth monthly","object":"price","owner_source_id":"0198f1a3-0b7c-7e25-9f41-6a2d8c3e5b19","plan_id":"0198f1a4-1d2e-7a40-9b31-5c6d7e8f9a0b","pricing_model":"fixed","properties":{"tier":"mid"},"properties_by_source":{"backend":{"tier":"mid"}},"tax_behavior":"exclusive","unit_amount":"50.00"}},"price_id":{"anyOf":[{"type":"null"},{"format":"uuid","type":"string"}],"description":"The price this item is on, when a source named one.","example":"0198f1a4-2e5f-7b80-a1c2-3d4e5f6a7b8c"},"price_name":{"anyOf":[{"type":"null"},{"type":"string"}],"description":"What the price this item is on is called, when a source named one.","example":"Growth monthly"},"pricing_model":{"description":"How the unit amount is arrived at.","enum":["fixed","tiered","metered"],"example":"fixed","type":"string"},"properties":{"anyOf":[{"type":"null"},{"additionalProperties":{"anyOf":[{"type":"string"},{"type":"number"},{"type":"boolean"}]},"type":"object"}],"description":"**Your** source's properties on this subscription item, as they stand now. What you write here is what you read back — another source's values never appear in it and can never be overwritten through it. `properties_by_source` has everyone's. When you have written a key through several of this subscription item's ids, its value is the most recent change any of them made.","example":{"seat_type":"standard"}},"properties_by_source":{"anyOf":[{"type":"null"},{"additionalProperties":{"additionalProperties":{"anyOf":[{"type":"string"},{"type":"number"},{"type":"boolean"}]},"description":"Property keys and their current values.","type":"object"},"type":"object"}],"description":"Every source's properties on this subscription item, keyed by source slug — including your own. Read-only: a write only ever reaches the source it is attributed to.","example":{"backend":{"seat_type":"standard"}}},"quantity":{"description":"How many units at this unit amount. A decimal, because a unit is not\nalways a whole seat: a billing system that meters usage sends 1.5 of\nsomething, and rounding it would misstate what the item is worth.\n","example":"25","format":"decimal","type":"string"},"response_metadata":{"anyOf":[{"type":"null"},{"additionalProperties":false,"properties":{"warnings":{"description":"Parts of this write that were skipped — a property whose value did not\nfit its type — and things it did that were worth saying, like merging\ntwo customers. The rest of the write landed. Absent when there is\nnothing to report.\n\nSend `on_warning: \"error\"` to have these refuse the write instead,\nand come back as `error.errors` with the same codes.\n","items":{"$ref":"#/components/schemas/Warning"},"type":"array"}},"type":"object"}],"description":"About this request rather than about the record. Absent when there is nothing to say."},"subscription":{"anyOf":[{"type":"null"},{"allOf":[{"$ref":"#/components/schemas/Subscription"}]}],"description":"The subscription this is an item of. Expandable.","example":{"annual_amount":"15000.00","billing_amounts":[{"interval":"month","interval_count":1,"net_amount":"1250.00"}],"currency_code":"USD","current_period_ends_at":"2026-11-01T10:04:00Z","customer_id":"0199a7c2-4e1b-7f38-b9d2-5a6c8e0f1d73","id":"0199a7c2-5b3e-7a14-8c6d-2f9e4b7a1c05","identities":["stripe:sub_1QhT5uV7wX9yZ2aB"],"monthly_amount":"1250.00","object":"subscription","owner_source_id":"0198f1a3-0b7c-7e25-9f41-6a2d8c3e5b19","properties":{"account_manager":"Priya Shah"},"properties_by_source":{"backend":{"account_manager":"Priya Shah"},"stripe":{"contract_id":"C-2025-118"}},"recorded_at":"2025-11-17T10:04:03Z","started_at":"2025-11-17T10:04:00Z","status":"active","trial_ends_at":"2025-12-01T10:04:00Z"}},"subscription_id":{"description":"The subscription this is an item of.","example":"0199a7c2-5b3e-7a14-8c6d-2f9e4b7a1c05","format":"uuid","type":"string"},"unit_amount":{"description":"What one unit costs per interval. Excludes tax and any discount.","example":"50.00","format":"decimal","type":"string"}},"required":["created_at","deleted","discount_amount","id","interval","interval_count","object","pricing_model","quantity","subscription_id","unit_amount"],"type":"object"},"UpdateCustomerResult":{"additionalProperties":false,"description":"A customer is a company or a person that pays you, or might. The record is\nresolved from what every source says about them, and carries the amounts\ncomputed from their subscriptions, such as `mrr` and `status`. The changes\nto those subscriptions are the run rate movements.\n","example":{"arr":"15000.00","churned_at":null,"country_code":"US","created_at":"2025-11-03T16:20:04Z","email":"billing@northwind.example.com","first_paying_at":"2025-12-01T10:04:00Z","first_seen_at":"2025-11-03T16:20:00Z","first_subscribed_at":"2025-11-17T10:04:00Z","id":"0199a7c2-4e1b-7f38-b9d2-5a6c8e0f1d73","identities":["stripe:cus_QX7bN2kLp4Rt9v","backend:acct_4821"],"mrr":"1250.00","name":"Northwind Labs","object":"customer","properties":{"seats":25},"properties_by_source":{"backend":{"seats":25},"stripe":{"email":"billing@northwind.example.com","name":"Northwind Labs"}},"signed_up_at":"2025-11-03T16:20:00Z","status":"paying"},"properties":{"arr":{"anyOf":[{"type":"null"},{"format":"decimal","type":"string"}],"description":"What this customer is worth a year, in the project's reporting currency,\nat today's rate: `mrr`, rounded to the cent, times twelve.\n","example":"15000.00"},"churned_at":{"anyOf":[{"type":"null"},{"format":"date-time","type":"string"}],"description":"When the current churn began — the instant `status` became `churned`.\nNull while the customer is trialing or active, and cleared when they\ncome back.\n"},"country":{"anyOf":[{"type":"null"},{"allOf":[{"$ref":"#/components/schemas/Country"}]}],"description":"The country the customer is billed in. Expandable.","example":{"code":"US","name":"United States","object":"country"}},"country_code":{"anyOf":[{"type":"null"},{"type":"string"}],"description":"The country the customer is billed in, by its ISO 3166-1 alpha-2 code —\nthe `country_code` property, resolved the same way as `name`, from the\nhighest-priority source whose value is a country's code. Expand `country`\nfor its name.\n","example":"US"},"created_at":{"description":"When Summerbase first recorded the customer.","example":"2025-11-03T16:20:04Z","format":"date-time","type":"string"},"email":{"anyOf":[{"type":"null"},{"maxLength":255,"type":"string"}],"description":"The customer's address, resolved the same way as `name`.","example":"billing@northwind.example.com"},"erasures":{"anyOf":[{"type":"null"},{"items":{"$ref":"#/components/schemas/CustomerErasure"},"type":"array"}],"description":"Every erasure of their personal data, oldest first. Expandable.","example":[{"customer_id":"0199a7c2-4e1b-7f38-b9d2-5a6c8e0f1d73","erased_properties":["customer.properties.backend.phone","customer.properties.stripe.name"],"id":"019a1f04-7c2d-7b3e-8a5f-1e2d3c4b5a69","object":"customer_erasure","requested_at":"2026-10-25T09:12:40Z"}]},"first_paying_at":{"anyOf":[{"type":"null"},{"format":"date-time","type":"string"}],"description":"The first instant this customer had revenue — when `status` first became\n`paying`, and what cohort analysis groups by. Set once and kept through\nchurn and reactivation. Which month it falls in depends on the timezone\nbeing reported in.\n","example":"2025-12-01T10:04:00Z"},"first_seen_at":{"anyOf":[{"type":"null"},{"format":"date-time","type":"string"}],"description":"The earliest instant anything was asserted about this customer, their\nsubscriptions or their items — the first thing we were told. Set once.\n","example":"2025-11-03T16:20:00Z"},"first_subscribed_at":{"anyOf":[{"type":"null"},{"format":"date-time","type":"string"}],"description":"The first instant one of this customer's subscriptions was live, free or\npaying — when `status` first became one of the two. Set once and kept\nthrough churn.\n","example":"2025-11-17T10:04:00Z"},"id":{"description":"The customer's Summerbase id. Anywhere a customer is addressed, one of its `identities` works too.","example":"0199a7c2-4e1b-7f38-b9d2-5a6c8e0f1d73","format":"uuid","type":"string"},"identities":{"anyOf":[{"type":"null"},{"items":{"type":"string"},"type":"array"}],"description":"What each source calls this customer, as `source:id`. Any entry addresses it — `stripe:cus_123`.","example":["stripe:cus_QX7bN2kLp4Rt9v","backend:acct_4821"]},"mrr":{"anyOf":[{"type":"null"},{"format":"decimal","type":"string"}],"description":"What this customer is worth a month, in the project's reporting currency,\nat today's rate, rounded to the cent.\n\nEvery kind of revenue counts, each change converted from the currency it was\nbilled in, so this amount moves when a rate does and when the project's\nreporting currency changes. The same amount a filter on `customer.mrr`\ncompares and a list sorted by `mrr` orders by.\n","example":"1250.00"},"name":{"anyOf":[{"type":"null"},{"maxLength":255,"type":"string"}],"description":"What the customer is called — the `name` property, resolved to the\nhighest-priority source that has one. Write `properties.name` to change it.\n","example":"Northwind Labs"},"object":{"description":"The object's type, always `customer`.","enum":["customer"],"example":"customer","type":"string"},"properties":{"anyOf":[{"type":"null"},{"additionalProperties":{"anyOf":[{"type":"string"},{"type":"number"},{"type":"boolean"}]},"type":"object"}],"description":"**Your** source's properties on this customer, as they stand now. What you write here is what you read back — another source's values never appear in it and can never be overwritten through it. `properties_by_source` has everyone's. When you have written a key through several of this customer's ids, its value is the most recent change any of them made.","example":{"seats":25}},"properties_by_source":{"anyOf":[{"type":"null"},{"additionalProperties":{"additionalProperties":{"anyOf":[{"type":"string"},{"type":"number"},{"type":"boolean"}]},"description":"Property keys and their current values.","type":"object"},"type":"object"}],"description":"Every source's properties on this customer, keyed by source slug — including your own. Read-only: a write only ever reaches the source it is attributed to.","example":{"backend":{"seats":25},"stripe":{"email":"billing@northwind.example.com","name":"Northwind Labs"}}},"response_metadata":{"anyOf":[{"type":"null"},{"additionalProperties":false,"properties":{"warnings":{"description":"Parts of this write that were skipped — a property whose value did not\nfit its type — and things it did that were worth saying, like merging\ntwo customers. The rest of the write landed. Absent when there is\nnothing to report.\n\nSend `on_warning: \"error\"` to have these refuse the write instead,\nand come back as `error.errors` with the same codes.\n","items":{"$ref":"#/components/schemas/Warning"},"type":"array"}},"type":"object"}],"description":"About this request rather than about the record. Absent when there is nothing to say."},"signed_up_at":{"anyOf":[{"type":"null"},{"format":"date-time","type":"string"}],"description":"When the customer signed up for the product — the `signed_up_at`\nproperty, resolved the same way as `name`. Write\n`properties.signed_up_at` to set it; a billing provider's source writes\nthe moment it first saw the customer, and a project's own source\noutranks it. A customer who has signed up and never subscribed is\n`trialing`.\n","example":"2025-11-03T16:20:00Z"},"status":{"description":"Where the customer stands with us, derived from what is live now: `paying`,\n`free`, `churned`, `trialing` or `lead`. A subscription is live while its\nstatus is `active`; paying and free are told apart by `mrr`, so `paying`\nmeans on a paid plan, whether or not an invoice has been settled. A customer\nwith nothing live who has subscribed before is `churned`; one on a trial, or\nwho has signed up and never subscribed, is `trialing`; anyone else is a\n`lead`. A change undone inside the project's collapse window never happened\nto the status either.\n","enum":["paying","free","churned","trialing","lead"],"example":"paying","type":"string"},"subscriptions":{"anyOf":[{"type":"null"},{"items":{"$ref":"#/components/schemas/Subscription"},"type":"array"}],"description":"Everything they are subscribed to, from every source. Expandable.","example":[{"annual_amount":"15000.00","billing_amounts":[{"interval":"month","interval_count":1,"net_amount":"1250.00"}],"currency_code":"USD","current_period_ends_at":"2026-11-01T10:04:00Z","customer_id":"0199a7c2-4e1b-7f38-b9d2-5a6c8e0f1d73","id":"0199a7c2-5b3e-7a14-8c6d-2f9e4b7a1c05","identities":["stripe:sub_1QhT5uV7wX9yZ2aB"],"monthly_amount":"1250.00","object":"subscription","owner_source_id":"0198f1a3-0b7c-7e25-9f41-6a2d8c3e5b19","properties":{"account_manager":"Priya Shah"},"properties_by_source":{"backend":{"account_manager":"Priya Shah"},"stripe":{"contract_id":"C-2025-118"}},"recorded_at":"2025-11-17T10:04:03Z","started_at":"2025-11-17T10:04:00Z","status":"active","trial_ends_at":"2025-12-01T10:04:00Z"}]}},"required":["created_at","id","object","status"],"type":"object"},"SubscriptionBillingAmount":{"additionalProperties":false,"properties":{"interval":{"description":"The unit these items bill in: `day`, `week`, `month` or `year`.","enum":["day","week","month","year"],"type":"string"},"interval_count":{"description":"How many of `interval` between charges: `3` and `month` is quarterly.","type":"integer"},"net_amount":{"description":"The items on this interval summed, each its `net_amount`, in the\nsubscription's `currency_code`.\n","format":"decimal","type":"string"}},"required":["interval","interval_count","net_amount"],"type":"object"}},"securitySchemes":{"bearerAuth":{"description":"An API key's secret, as `Authorization: Bearer sb_...`. A key is scoped to one project and carries that scope with it, so the scope headers are unnecessary and are refused if they name anything else.","scheme":"bearer","type":"http"}}},"info":{"description":"The Summerbase API is how your own code sends Summerbase the revenue data\nit measures — customers, subscriptions, prices, plans and their properties — and\nreads back what is computed from it: MRR, the Monthly Run Rate, ARR, the\nAnnual Run Rate, churn, retention, and the run rate movements behind them.\n\nRequests go to `https://api.summerbase.com`, and every path starts with\n`/v1`.\n\nThe API is in preview, and no part of it is stable yet. Any endpoint,\nfield or behavior may change, including in ways that break an existing\nintegration, and without notice.\n\n## Authentication\n\nEvery request is authenticated with an API key, sent as a bearer token in\nthe `Authorization` header:\n\n```sh\ncurl https://api.summerbase.com/v1/project \\\n  -H \"Authorization: Bearer sb_us1_k7hq2mxa_vq3nr8tw2yz5cf6hk2mp7sx4bd3gj5lq2wn6tr4yv7zc3fh5km2pq\"\n```\n\nA key's role decides what it may do: a `viewer` key reads, and a\n`writer` key writes as well. Everything a key writes is attributed to\nthe source it is bound to. The secret is shown once, when the key is\ncreated. Keep it on a server: anyone who has it can do what the key can.\n\nKeys are created and revoked in Summerbase, by an admin of the project,\nand never through the API. Revoking a key stops it working immediately,\nfor every request after.\n\nA request with no key, or with one that is mistyped or revoked, is\nrefused with `401`.\n\n## Organizations and projects\n\nAn organization is a company using Summerbase, and it holds one or more\nprojects. A project holds one product's revenue data, and nearly every\nendpoint works in one project.\n\nAn API key belongs to one project, and a request made with it works in\nthat project. It needs no header to say so. The `Summerbase-Project` and\n`Summerbase-Organization` headers are there for requests made on behalf\nof a person signed in to Summerbase, who may belong to several\norganizations and projects, and they name which one the request is about.\nA request made with a key that sends either header naming a different\nproject or organization is refused with `403` and the code\n`outside_key_scope`.\n\nSome endpoints are about the organization or the project's setup rather\nthan its revenue data, and need a person with a role in the organization.\nA key's role in its organization is the lowest there is, so a request made\nwith a key is refused with `403` by:\n\n- listing and creating projects, and changing the project's settings,\n- creating organizations and changing them,\n- memberships and invitations,\n- creating and changing sources,\n- `GET /v1/me` and `PATCH /v1/me`, which are about the person a request\n  is made as, where a key is nobody.\n\nA key may read its own organization with `GET /v1/organization`.\n\n## Requests and responses\n\nA request with a body sends it as JSON, with\n`Content-Type: application/json`. A body sent as anything else is refused\nwith `415`.\n\nA response is flat JSON. A record is its own fields, with an `object`\nfield naming what it is, and there is no envelope to unwrap:\n\n```sh\ncurl https://api.summerbase.com/v1/customers/backend:acct_4821 \\\n  -X PATCH \\\n  -H \"Authorization: Bearer $SUMMERBASE_API_KEY\" \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\"properties\": {\"name\": \"Northwind Labs\", \"seats\": 25}}'\n```\n\n```json\n{\n  \"id\": \"0199a7c2-4e1b-7f38-b9d2-5a6c8e0f1d73\",\n  \"object\": \"customer\",\n  \"name\": \"Northwind Labs\",\n  \"status\": \"paying\",\n  \"mrr\": \"1250.00\",\n  \"arr\": \"15000.00\"\n}\n```\n\nA field with nothing in it is `null`. A field that holds another object\nis left out unless the request asks for it; see Expanding responses.\n\n## Formats\n\n- **Times** are ISO 8601. Responses give them in UTC, as\n  `2026-06-12T15:47:09.512031Z`; a request may send them with any\n  offset.\n- **Amounts of money** are decimal strings, such as `\"1250.00\"`, so that\n  no rounding happens on the way in or out. Send them as strings too.\n  An amount is in the currency its record names: a subscription's\n  `currency_code`, or a project's `reporting_currency_code` for a\n  customer's `mrr` and the metrics.\n- **Currencies** are ISO 4217 codes, such as `USD`. `GET /v1/currencies`\n  lists the ones Summerbase holds exchange rates for.\n- **Countries** are ISO 3166-1 alpha-2 codes, such as `US`.\n- **Timezones** are IANA names, such as `America/New_York`.\n  `GET /v1/timezones` lists them. An offset like `+02:00` or an\n  abbreviation like `CET` is refused.\n\n## Identifying records\n\nCustomers, subscriptions, subscription items, prices and plans each have a\nSummerbase id, a UUID. Wherever the API takes the id of one of them — in\na path, in `customer_id` or `subscription_id`, or as a subscription\nitem's `id` — it takes a source's own id for it as well, written\n`<source slug>:<id in that source>`:\n\n```sh\ncurl https://api.summerbase.com/v1/customers/stripe:cus_QX7bN2kLp4Rt9v \\\n  -H \"Authorization: Bearer $SUMMERBASE_API_KEY\"\n```\n\nA record's `identities` lists every source's id for it. An id with no\nsource in front of it is always a Summerbase id.\n\nA `PATCH` to a record named by one of your source's own ids creates it\nwhen it does not exist yet, so the first write and every later one are\nthe same request. A Summerbase id that names nothing is answered with\n`404`, since nothing can be created under an id Summerbase did not\nassign.\n\nEverything else — projects, integrations, API keys, memberships — is\nnamed by its Summerbase id alone.\n\n## Sources and ownership\n\nAn integration is a system the project is connected to, and one that\nsends Summerbase data is a **source** — its `is_source` says so. A\nprovider's integration, such as Stripe, fetches its data itself once it\nis connected. A custom integration is one you write to, and every API key\nthat writes is bound to one: everything the key writes is attributed to\nthat source, and nothing in a request can choose another. What your team\nwrites in Summerbase itself is the project's Summerbase integration, a\nsource like the others.\n\nA subscription, its items and a plan each live in one source, named by\n`owner_source_id`. Only that source may change the record's own fields —\na subscription's status, dates, currency and items, an item's amounts, a\nplan's name — and a write from any other source that tries is refused\nwith `400` and the code `not_the_owner`. Any source may still write\n`properties` and `identities` on it.\n\nCreating a subscription claims it for your source, so a create says so\nwith `\"claim_ownership\": true`, and one without it is refused with `404`.\nThat stops a write meant to annotate a subscription another system has\nnot reported yet from creating it under the wrong owner:\n\n```json\n{\n  \"customer_id\": \"backend:acct_4821\",\n  \"claim_ownership\": true,\n  \"status\": \"active\",\n  \"currency_code\": \"USD\",\n  \"items\": [\n    {\"id\": \"backend:item_9001\", \"price_id\": \"backend:growth-monthly\", \"quantity\": \"25\", \"unit_amount\": \"50.00\", \"interval\": \"month\"}\n  ]\n}\n```\n\nA customer belongs to no one source. Its `name`, `email`,\n`signed_up_at` and `country_code` are properties every source may write,\nand the record takes each from the source with the lowest `priority`\nthat has one.\n\n## Properties\n\nProperties are what a source knows about a record beyond its own fields:\na customer's seat count, a subscription's account manager. Customers,\nsubscriptions, subscription items, prices and plans all take them, as\n`properties` on a write:\n\n```json\n{\"properties\": {\"seats\": 25, \"plan_tier\": \"growth\", \"trial_extended\": true}}\n```\n\n- Each source's properties are its own. `properties` on a record is your\n  source's, and `properties_by_source` holds every source's, keyed by\n  source slug. A write reaches only your source's.\n- A key is registered the first time it is written, and a value is a\n  `string`, a `number`, a `boolean` or a `datetime`. The first value\n  decides the type, unless the key was declared first with\n  `POST /v1/property_definitions`.\n- Only the keys a write names change. `null` clears one.\n- A value may also be an object: `{\"value\": \"2026-01-01T00:00:00Z\",\n  \"type\": \"datetime\"}` states its type, and `\"operation\"` may be\n  `increment` to add to a number or `set_once` to keep a value already\n  there.\n\nA value that does not fit its key's type is skipped, with a warning; see\nWarnings.\n\n## Filters\n\nEvery metric, and searching customers, take a `filter`: a condition, and\nonly the customers it matches are counted or listed.\n\n*Example: Customers in the US with an MRR above 1,000.*\n\n```json\n{\n  \"filter\": {\n    \"all\": [\n      {\"key\": \"customer.country_code\", \"equals\": \"US\"},\n      {\"key\": \"customer.mrr\", \"greater_than\": 1000}\n    ]\n  }\n}\n```\n\n### Comparing a value\n\nThe simplest condition compares one value. It has a `key`, which says what\nto look at, and one operator, which says how to compare it.\n\n*Example: Customers in the US.*\n\n```json\n{\"key\": \"customer.country_code\", \"equals\": \"US\"}\n```\n\nA key starts with what it is about — `customer`, `subscription` or\n`subscription_item` — then the field: `customer.status`,\n`subscription.currency_code`, `subscription_item.plan_id`.\n\nTo use what one of your sources says about a record, put `properties` and\nthe source's slug in between: `customer.properties.hubspot.industry`.\n\n`subscription.owner_source_id` is the source a subscription lives in, so\na filter on it finds the customers billed through one source, and a\nbreakdown by it splits a figure by source. It takes a source's `id`.\n\n`customer.id`, `subscription.id` and `subscription_item.id` match one\nrecord. Give any of its IDs: its Summerbase ID, or what one of your\nsources calls it, like `stripe:cus_123`. Compare them with `equals` or\n`not_equals`. A breakdown by `customer.id` splits a metric by customer,\nshowing each one by name.\n\n[`GET /v1/fields`](/docs/api/operations/list-fields) lists every key your\nproject can use.\n\nThe value is read as whatever the key holds: text, a number, `true` or\n`false`, or a date. A date is ISO 8601 — `2026-03-01` or\n`2026-03-01T12:00:00Z` — and is UTC unless it says otherwise. It can also\nbe relative to now, the way a metric's `from` and `to` are: `now-3M` is\nthree months ago to the second, and `now-1M/M` the start of last month.\nA rounding always goes down to the start of its unit, in the project's\nreporting timezone, and every `now` in one request is the same moment. A\nkey that does not exist, or a value that does not fit the key, is refused\nwith an error saying why.\n\n*Example: Customers who churned in the last three months.*\n\n```json\n{\"key\": \"customer.churned_at\", \"greater_than_or_equals\": \"now-3M\"}\n```\n\n#### Equals\n\nMatches if the field equals a specific value. Text is compared ignoring\ncase.\n\n*Example: Customers in the US.*\n\n```json\n{\"key\": \"customer.country_code\", \"equals\": \"US\"}\n```\n\n#### Does not equal\n\nMatches if the field is anything but a specific value, or has no value at\nall.\n\n*Example: Customers HubSpot does not put in finance, including those it says nothing about.*\n\n```json\n{\"key\": \"customer.properties.hubspot.industry\", \"not_equals\": \"finance\"}\n```\n\n#### Contains text\n\nMatches if the field contains a piece of text, ignoring case.\n\n*Example: Customers with an email address at acme.com.*\n\n```json\n{\"key\": \"customer.email\", \"contains\": \"@acme.com\"}\n```\n\nFor the opposite, wrap it in a `not`.\n\n*Example: Customers without an email address at acme.com.*\n\n```json\n{\"not\": {\"key\": \"customer.email\", \"contains\": \"@acme.com\"}}\n```\n\n#### Greater than\n\nMatches if the field is greater than a value. For a number or a date.\n\n*Example: Customers with an MRR above 1,000, in the project's reporting currency.*\n\n```json\n{\"key\": \"customer.mrr\", \"greater_than\": 1000}\n```\n\n#### Greater than or equal to\n\nMatches if the field is greater than or equal to a value.\n\n*Example: Customers with an item of ten seats or more.*\n\n```json\n{\"key\": \"subscription_item.quantity\", \"greater_than_or_equals\": 10}\n```\n\n#### Less than\n\nMatches if the field is less than a value. For a date, that means before\nit.\n\n*Example: Customers who first paid before 2026.*\n\n```json\n{\"key\": \"customer.first_paying_at\", \"less_than\": \"2026-01-01\"}\n```\n\n#### Less than or equal to\n\nMatches if the field is less than or equal to a value.\n\n*Example: Customers with an ARR of 12,000 or less.*\n\n```json\n{\"key\": \"customer.arr\", \"less_than_or_equals\": 12000}\n```\n\n#### Has a value\n\nMatches if the field has any value at all.\n\n*Example: Customers with an email address.*\n\n```json\n{\"key\": \"customer.email\", \"present\": true}\n```\n\n#### Has no value\n\nMatches if the field is empty.\n\n*Example: Customers HubSpot has not given an industry.*\n\n```json\n{\"key\": \"customer.properties.hubspot.industry\", \"present\": false}\n```\n\n### Matching every condition\n\n`all` is an AND: it matches when every condition in its list matches.\n\n*Example: Paying customers in the US.*\n\n```json\n{\n  \"all\": [\n    {\"key\": \"customer.status\", \"equals\": \"paying\"},\n    {\"key\": \"customer.country_code\", \"equals\": \"US\"}\n  ]\n}\n```\n\n### Matching any condition\n\n`any` is an OR: it matches when at least one condition in its list matches.\n\n*Example: Customers in the US or Denmark.*\n\n```json\n{\n  \"any\": [\n    {\"key\": \"customer.country_code\", \"equals\": \"US\"},\n    {\"key\": \"customer.country_code\", \"equals\": \"DK\"}\n  ]\n}\n```\n\n### Excluding a condition\n\n`not` is a NOT: it matches when the condition inside it does not.\n\n*Example: Customers outside the US and Denmark.*\n\n```json\n{\n  \"not\": {\n    \"any\": [\n      {\"key\": \"customer.country_code\", \"equals\": \"US\"},\n      {\"key\": \"customer.country_code\", \"equals\": \"DK\"}\n    ]\n  }\n}\n```\n\n### Nesting conditions\n\n`all`, `any` and `not` can contain any condition, each other included.\n\n*Example: Paying customers in the US or Denmark, except those at acme.com.*\n\n```json\n{\n  \"all\": [\n    {\"key\": \"customer.status\", \"equals\": \"paying\"},\n    {\n      \"any\": [\n        {\"key\": \"customer.country_code\", \"equals\": \"US\"},\n        {\"key\": \"customer.country_code\", \"equals\": \"DK\"}\n      ]\n    },\n    {\"not\": {\"key\": \"customer.email\", \"contains\": \"@acme.com\"}}\n  ]\n}\n```\n\nA filter is at most four levels deep, with at most twenty-five comparisons.\n\n### Subscriptions and items\n\nA filter is about customers, and a customer can have several subscriptions.\nTake two customers:\n\n- **Customer1** has one subscription, active and in euros.\n- **Customer2** has two: one active and in dollars, and one canceled and in\n  euros.\n\nA condition about a subscription matches a customer who has a subscription\nthat matches it, and each condition is checked on its own. So the filter\nbelow matches both customers: Customer1's subscription is active and in\neuros, and Customer2 has an active subscription and a subscription in\neuros, though not the same one.\n\n*Example: Customers with an active subscription, and a subscription in euros. Matches Customer1 and Customer2.*\n\n```json\n{\n  \"all\": [\n    {\"key\": \"subscription.status\", \"equals\": \"active\"},\n    {\"key\": \"subscription.currency_code\", \"equals\": \"EUR\"}\n  ]\n}\n```\n\nTo ask for one subscription that is both, put the conditions in a\n`has_subscription`. Everything inside it is about the same subscription.\n\n*Example: Customers with an active subscription in euros. Matches Customer1 alone.*\n\n```json\n{\n  \"has_subscription\": {\n    \"all\": [\n      {\"key\": \"subscription.status\", \"equals\": \"active\"},\n      {\"key\": \"subscription.currency_code\", \"equals\": \"EUR\"}\n    ]\n  }\n}\n```\n\n`has_subscription_item` does the same for an item. Inside a\n`has_subscription` it is an item of that subscription; on its own, an item\nof any subscription.\n\n*Example: Customers with an active subscription that has an item of ten seats or more on the Pro plan.*\n\n```json\n{\n  \"has_subscription\": {\n    \"all\": [\n      {\"key\": \"subscription.status\", \"equals\": \"active\"},\n      {\n        \"has_subscription_item\": {\n          \"all\": [\n            {\"key\": \"subscription_item.plan_id\", \"equals\": \"stripe:prod_pro\"},\n            {\"key\": \"subscription_item.quantity\", \"greater_than_or_equals\": 10}\n          ]\n        }\n      }\n    ]\n  }\n}\n```\n\n*Example: Customers with no active subscription in euros. Matches Customer2.*\n\n```json\n{\n  \"not\": {\n    \"has_subscription\": {\n      \"all\": [\n        {\"key\": \"subscription.status\", \"equals\": \"active\"},\n        {\"key\": \"subscription.currency_code\", \"equals\": \"EUR\"}\n      ]\n    }\n  }\n}\n```\n\nA filter contains at most five of them, counting each condition about a\nsubscription or an item outside one as one of its own.\n\n#### In a metric\n\nA metric adds up revenue. There, a condition about a subscription or an\nitem outside a `has_subscription` narrows the revenue counted to the\nsubscriptions or items it matches.\n\n*Example: The MRR of subscriptions in euros alone.*\n\n```json\n{\n  \"measure\": \"mrr\",\n  \"filter\": {\"key\": \"subscription.currency_code\", \"equals\": \"EUR\"}\n}\n```\n\nInside a `has_subscription`, it asks about the customer instead, and counts\nall of their revenue once they have such a subscription.\n\n*Example: The whole MRR of customers who have a subscription in euros.*\n\n```json\n{\n  \"measure\": \"mrr\",\n  \"filter\": {\n    \"has_subscription\": {\"key\": \"subscription.currency_code\", \"equals\": \"EUR\"}\n  }\n}\n```\n\nA metric that counts customers, such as `paying_customers`, has no\nsubscription of its own to narrow to, and takes a condition about a\nsubscription or an item only inside a `has_subscription`.\n\n### When a value is read\n\nA value can change: a customer moves country, a subscription is canceled.\nA comparison reads the value at one moment, and its `at` says which:\n\n| `at` | Reads the value |\n|---|---|\n| `\"now\"` | as it is today. What a customer search reads when `at` is left out. |\n| A date or a date and time | as it was then. A date means the end of that day, in the project's reporting timezone. |\n| Relative to now, such as `\"now-3M\"` | as it was then, counted from the moment of the request. A rounding goes to the start of its unit: `\"now/M\"` is the first moment of this month. |\n| `\"window\"` | as it was in each period: at its end for a figure like MRR, and when the money moved for a movement. Metrics only, and what a metric reads when `at` is left out. |\n\n*Example: Customers who were paying on March 1, 2026, whatever they are today.*\n\n```json\n{\"key\": \"customer.status\", \"equals\": \"paying\", \"at\": \"2026-03-01\"}\n```\n\nIn a metric, leaving `at` out reads each period as it was. Say a customer\nmoved from Denmark to the US in June. This counts their MRR as American\nfrom June on, and as Danish before.\n\n*Example: MRR per month of customers in the US, as they were each month.*\n\n```json\n{\n  \"measure\": \"mrr\",\n  \"from\": \"2026-01-01\",\n  \"to\": \"2026-12-31\",\n  \"interval\": \"month\",\n  \"filter\": {\"key\": \"customer.country_code\", \"equals\": \"US\"}\n}\n```\n\nWith `\"at\": \"now\"`, every month reads the value as it is today instead, so the\nsame customer counts as American in every month of the year.\n\n*Example: MRR per month of the customers who are in the US today.*\n\n```json\n{\n  \"measure\": \"mrr\",\n  \"from\": \"2026-01-01\",\n  \"to\": \"2026-12-31\",\n  \"interval\": \"month\",\n  \"filter\": {\"key\": \"customer.country_code\", \"equals\": \"US\", \"at\": \"now\"}\n}\n```\n\n## Response metadata\n\nA response may carry a `response_metadata` object beside the record's\nown fields. It is about the request rather than the record, so it is\nnever part of a later read of the same record, and it is left out when\nthere is nothing to say. It carries `warnings`, on the writes to\ncustomers, subscriptions, subscription items, prices and plans: what the write\ncould not do. See Warnings. A dry run of an erasure carries\n`dry_run: true`, so that its answer cannot be mistaken for an erasure\nthat happened.\n\n## Warnings\n\nA write that carries revenue is not refused over a part of it that cannot\nbe used. The rest lands, and each part that did not is a warning in\n`response_metadata.warnings`, with a `code`, the `field` it came from and\na `message`:\n\n```json\n{\n  \"response_metadata\": {\n    \"warnings\": [\n      {\n        \"code\": \"property_type_mismatch\",\n        \"field\": \"properties.seats\",\n        \"message\": \"`seats` holds number values in this source, and `many` is not one.\"\n      }\n    ]\n  }\n}\n```\n\nThe codes are `property_type_mismatch`, a value that does not fit its\nkey's type, and `property_key_refused`, a key that could not be\nregistered.\n\nTo have a write refused rather than partly applied, send\n`\"on_warning\": \"error\"`. Nothing is saved, and the warnings come back as\nerrors with the same codes. That suits a form, where somebody is waiting\nto be told what to fix; an integration that sends data usually wants the\ndefault, `warn`.\n\n## Pagination\n\nAn endpoint that lists records answers with a list object, one page at a\ntime:\n\n```json\n{\n  \"object\": \"list\",\n  \"data\": [\n    {\"id\": \"0199a7c2-4e1b-7f38-b9d2-5a6c8e0f1d73\", \"object\": \"customer\"}\n  ],\n  \"has_more\": true,\n  \"next_cursor\": \"g3QAAAABZAACaWRtAAAAJDAxOTlhN2MyLTRlMWItN2YzOC1iOWQyLTVhNmM4ZTBmMWQ3Mw\"\n}\n```\n\n- `data` holds the page's records.\n- `has_more` says whether another page follows.\n- `next_cursor` is what to send as `cursor` to read the next page, and\n  `null` on the last.\n- `total_count`, how many records match across every page, is there only\n  when the request asked for it.\n\nA list takes these inputs, in the query string of a `GET` and in the body\nof a search:\n\n- `limit`: how many records a page holds, from 1 to 100. The default is\n  20.\n- `cursor`: the `next_cursor` of the page before.\n- `include_total_count`: `true` to add `total_count`. It is answered on\n  the first page only, and refused beside a `cursor`, since it is the\n  same number on every page; keep the first page's.\n\nTo read every page, send each page's `next_cursor` back until `has_more`\nis `false`:\n\n```js\nconst customers = []\nlet cursor = null\n\ndo {\n  const response = await fetch(\"https://api.summerbase.com/v1/customers/search\", {\n    method: \"POST\",\n    headers: {\n      Authorization: `Bearer ${process.env.SUMMERBASE_API_KEY}`,\n      \"Content-Type\": \"application/json\",\n    },\n    body: JSON.stringify(cursor ? { limit: 100, cursor } : { limit: 100 }),\n  })\n  const page = await response.json()\n  customers.push(...page.data)\n  cursor = page.has_more ? page.next_cursor : null\n} while (cursor)\n```\n\n## Expanding responses\n\nA field that holds another object, such as a subscription's `customer`,\nis left out of a response unless the request asks for it with\n`expand[]`. Each object's page marks those fields as expandable, and each\nendpoint lists the values it takes.\n\nA dotted path reaches into what an expanded object holds in turn:\n`items.price` expands a subscription's items and each item's price, and\n`items.price.plan` the plan of each of those prices too. A path expands\neverything along it, so `items.price` needs no `items` beside it.\n\n```sh\ncurl \"https://api.summerbase.com/v1/subscriptions/backend:sub_7731?expand[]=customer&expand[]=items.price\" \\\n  -H \"Authorization: Bearer $SUMMERBASE_API_KEY\"\n```\n\n```json\n{\n  \"id\": \"0199a7c2-5b3e-7a14-8c6d-2f9e4b7a1c05\",\n  \"object\": \"subscription\",\n  \"customer_id\": \"0199a7c2-4e1b-7f38-b9d2-5a6c8e0f1d73\",\n  \"customer\": {\n    \"id\": \"0199a7c2-4e1b-7f38-b9d2-5a6c8e0f1d73\",\n    \"object\": \"customer\",\n    \"name\": \"Northwind Labs\"\n  },\n  \"items\": [\n    {\n      \"id\": \"0199a7c2-5b41-7c62-9d3e-6a1f8b2c4d97\",\n      \"object\": \"subscription_item\",\n      \"quantity\": \"25\",\n      \"price_id\": \"0199a7c2-3c8d-7b51-a4e2-7d9f1b3c5e28\",\n      \"price\": {\n        \"id\": \"0199a7c2-3c8d-7b51-a4e2-7d9f1b3c5e28\",\n        \"object\": \"price\",\n        \"name\": \"Growth monthly\"\n      }\n    }\n  ]\n}\n```\n\n- Not expanded, the field is absent. A field that points at one object\n  has its id beside it either way, as `customer_id` is here; one that\n  holds several, such as `items`, has nothing until it is expanded.\n- Expanded, a field that points at nothing is `null`, and one that holds\n  several is a list.\n- On a list endpoint, `expand[]` expands that field on every record in\n  `data`. A search takes the same values as `expand` in its body.\n- Each step of a path can expand what that object's own endpoints offer,\n  and each endpoint lists every path it takes. A path is at most four\n  steps, a request takes at most ten, and no path may come back to an\n  object already on it — `items.subscription` on a subscription is the\n  subscription you asked for. Anything else is refused with `400` and the\n  code `invalid_expand`, which names what was wrong.\n\n## Errors\n\nA request that fails is answered with an error status and a single\n`error` object:\n\n```json\n{\n  \"error\": {\n    \"type\": \"invalid_request_error\",\n    \"code\": \"not_found\",\n    \"message\": \"No customer in this project is identified by `stripe:cus_QX7bN2kLp4Rt9v`.\"\n  }\n}\n```\n\n- `type` is the class of failure.\n- `code` says what went wrong, and is stable enough to branch on.\n- `message` explains it in a sentence that is safe to show a person.\n- `param` names the input at fault, when the failure is about one. It is\n  absent otherwise.\n\n### Statuses\n\n| Status | Type | When |\n| --- | --- | --- |\n| `400` | `invalid_request_error` | An input is missing, has a value it cannot take, or is not one the endpoint takes. `param` names it. |\n| `401` | `authentication_error` | The request carries no API key, or one that is mistyped or revoked. |\n| `403` | `permission_error` | The key may not do this: its role is too low, or a scope header names another project. |\n| `404` | `invalid_request_error` | The path does not exist, or what it or an input names does not exist in the project. |\n| `409` | `invalid_request_error` | Another write to the same record got there at the same moment. |\n| `429` | `rate_limit_error` | A rate limit was reached. See Rate limits. |\n| `500` | `api_error` | Something went wrong on our side. Retry later. |\n\nA `409` with the code `conflict` can happen on any write. Nothing was\nsaved, and the same request can simply be sent again.\n\nAny request can fail in these ways, so an endpoint's page does not list\nthem again. What an endpoint requires — which source may change a field,\nsay — is in its description and its fields', and a request that does not\nfollow it is refused.\n\n### More than one problem\n\nWhen more than one thing was wrong, such as two invalid fields in one\nbody, every one of them is listed in `error.errors`, each with its own\n`type`, `code`, `message` and `param`. The fields of `error` itself are\nthe first of them, so a client that reads only those still reads a real\nerror:\n\n```json\n{\n  \"error\": {\n    \"type\": \"invalid_request_error\",\n    \"code\": \"invalid\",\n    \"message\": \"`maintainer` is not one of `viewer`, `writer`, `admin`, `owner`\",\n    \"param\": \"project_role\",\n    \"errors\": [\n      {\"type\": \"invalid_request_error\", \"code\": \"invalid\", \"message\": \"`maintainer` is not one of `viewer`, `writer`, `admin`, `owner`\", \"param\": \"project_role\"},\n      {\"type\": \"invalid_request_error\", \"code\": \"required\", \"message\": \"`name` is required.\", \"param\": \"name\"}\n    ]\n  }\n}\n```\n\n### Codes\n\nThe codes an integration most often has reason to branch on:\n\n- `not_found` (`404`): what the path or an input names does not exist in\n  the project.\n- `conflict` (`409`): another write got there first. Send the request\n  again.\n- `not_the_owner` (`400`): the record lives in another source. See\n  Sources and ownership.\n- `required`, `invalid` and `unknown_parameter` (`400`): an input is\n  missing, has a value it cannot take, or is not one the endpoint takes.\n- `invalid_api_key` (`401`): the key is mistyped or has been revoked.\n- `outside_key_scope` (`403`): a scope header names a project or an\n  organization other than the key's.\n- `forbidden` (`403`): the key's role does not allow this.\n- `requests_per_second`, `analytics_requests_per_minute` and\n  `concurrent_analytics_requests` (`429`): the rate limit reached.\n\n## Rate limits\n\nAn organization's API keys share one allowance, and people signed in to\nSummerbase have another, so an integration's limits do not depend on who\nis using the app:\n\n- **50 requests a second**, any request, refilling continuously.\n- **300 analytics requests a minute**, on top of the 50 a second. An\n  analytics request is one that computes from the revenue history — the\n  metrics, the customers behind a value, customer searches and revenue\n  movements — and its operation says so with `x-analytics-request`.\n- **20 analytics requests running at once.**\n\nEvery response counted says where it stands: `RateLimit-Limit`,\n`RateLimit-Remaining`, and `RateLimit-Reset`, the seconds until the\nallowance is whole again. A request over a limit is answered `429` with\n`Retry-After` in seconds and an error whose `code` names the limit:\n`requests_per_second`, `analytics_requests_per_minute` or\n`concurrent_analytics_requests`. Nothing was done, so the request can be\nsent again once that time has passed.\n\n## Versioning\n\nThe version is part of every path: `/v1`. There is one version, and it\nis a preview: nothing in it is stable yet, and any endpoint, field or\nbehavior may change without notice and without a new version. There is\nno header to pin a version, and no changelog yet.\n","title":"Summerbase API","version":"1.0.0"},"openapi":"3.1.0","paths":{"/v1/me":{"get":{"callbacks":{},"deprecated":false,"description":"Fetch the signed-in user.","operationId":"get_current_user","parameters":[{"description":"The organization to work within. Optional for an API key, which is already scoped to one; naming a different one is refused.","in":"header","name":"Summerbase-Organization","required":false,"schema":{"format":"uuid","type":"string"}},{"description":"The project to work within. Its organization is implied, so this alone is enough. Optional for an API key, which is already scoped to one.","in":"header","name":"Summerbase-Project","required":false,"schema":{"format":"uuid","type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/User"}}},"description":"Returns the user object for the person the request is made as."},"default":{"$ref":"#/components/responses/Error"}},"summary":"Get the current user","tags":["Users"]},"patch":{"callbacks":{},"deprecated":false,"description":"Update the signed-in user's profile.","operationId":"update_current_user","parameters":[{"description":"The organization to work within. Optional for an API key, which is already scoped to one; naming a different one is refused.","in":"header","name":"Summerbase-Organization","required":false,"schema":{"format":"uuid","type":"string"}},{"description":"The project to work within. Its organization is implied, so this alone is enough. Optional for an API key, which is already scoped to one.","in":"header","name":"Summerbase-Project","required":false,"schema":{"format":"uuid","type":"string"}}],"requestBody":{"content":{"application/json":{"example":{"name":"Maya Chen"},"schema":{"additionalProperties":false,"properties":{"name":{"description":"The name on the account.","example":"Maya Chen","type":"string"}},"type":"object"}}},"required":false},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/User"}}},"description":"Returns the updated user object."},"default":{"$ref":"#/components/responses/Error"}},"summary":"Update the current user","tags":["Users"]}},"/v1/project":{"get":{"callbacks":{},"deprecated":false,"description":"Fetch the project the request is working in — the one named by\n`Summerbase-Project`, or the one an API key is issued for.\n","operationId":"get_current_project","parameters":[{"description":"Relationships to include in the response instead of fetching separately. A dotted path expands what an expanded record can expand in turn — at most 4 deep, 10 paths a request, and never back to a record already on the path.","in":"query","name":"expand[]","required":false,"schema":{"items":{"enum":["organization","reporting_currency"],"type":"string"},"maxItems":10,"type":"array"}},{"description":"The organization to work within. Optional for an API key, which is already scoped to one; naming a different one is refused.","in":"header","name":"Summerbase-Organization","required":false,"schema":{"format":"uuid","type":"string"}},{"description":"The project to work within. Its organization is implied, so this alone is enough. Optional for an API key, which is already scoped to one.","in":"header","name":"Summerbase-Project","required":false,"schema":{"format":"uuid","type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Project"}}},"description":"Returns the project object for the project the request works in."},"default":{"$ref":"#/components/responses/Error"}},"summary":"Get the current project","tags":["Projects"]},"patch":{"callbacks":{},"deprecated":false,"description":"Rename the project, or change how its metrics are reported: the\ncurrency and timezone, the cadence, when churn counts and how close\ntogether two changes count as one.\n\nRenaming does not move the slug, which is moved on its own.\n\nChanging a setting that decides how the project is measured recomputes\nevery customer in it, and every number moves as it goes. There is a limit\non how often that may be asked for.\n\nTo move the project's slug, see [`PATCH /v1/project/slug`](/docs/api/operations/move-project-slug).","operationId":"update_project","parameters":[{"description":"Relationships to include in the response instead of fetching separately. A dotted path expands what an expanded record can expand in turn — at most 4 deep, 10 paths a request, and never back to a record already on the path.","in":"query","name":"expand[]","required":false,"schema":{"items":{"enum":["organization","reporting_currency"],"type":"string"},"maxItems":10,"type":"array"}},{"description":"The organization to work within. Optional for an API key, which is already scoped to one; naming a different one is refused.","in":"header","name":"Summerbase-Organization","required":false,"schema":{"format":"uuid","type":"string"}},{"description":"The project to work within. Its organization is implied, so this alone is enough. Optional for an API key, which is already scoped to one.","in":"header","name":"Summerbase-Project","required":false,"schema":{"format":"uuid","type":"string"}}],"requestBody":{"content":{"application/json":{"example":{"reporting_currency_code":"USD","reporting_timezone":"America/New_York"},"schema":{"additionalProperties":false,"properties":{"churn_timing":{"description":"When a subscription's revenue stops counting: `at_expiration`, when it\nends, or `at_cancellation`, when a cancellation is booked for it.\n\nUnder `at_cancellation` a subscription with `cancels_at` set is worth\nnothing from the moment the source said so, though it is still in\nservice: the customer's MRR drops then, the movement is churn, or\ncontraction if something else is still live, and a customer with nothing\nelse live is `churned`. Withdrawing the cancellation brings the revenue\nback. A trial booked to end loses nothing, having nothing to lose. A\nStripe import dates a booking at Stripe's `canceled_at`, which holds only\nthe latest request, so an earlier or withdrawn one is not in the history.\n\nChanging it moves the metrics of every customer in the project.\n","enum":["at_expiration","at_cancellation"],"type":"string"},"movement_collapse_window_days":{"description":"How close together two changes to a customer's revenue have to be to count\nas one.\n\nSigning up at $100 and upgrading to $500 the next day is $500 of new\nrevenue, not $100 of new plus $400 of expansion, and a cancellation\nreversed within the window is neither a churn nor a reactivation. The\ncollapsed movement is dated at the first change in the window, so a\ncustomer who arrives on the 30th and upgrades on the 2nd still arrived in\nthe first month. `0` records every change as it happened.\n\nAn elapsed duration, not a calendar one: seven days is 168 hours wherever\nthe project reports from. Changing it moves the metrics of every customer in\nthe project, and a closed period can move while it is still inside the\nwindow.\n","maximum":90,"minimum":0,"type":"integer"},"name":{"description":"What the project is called.","maxLength":255,"minLength":1,"type":"string"},"reporting_cadence":{"description":"Which cadence the project quotes its metrics at: `monthly`, MRR, or\n`annual`, ARR.\n\nEvery measure is answered at either cadence whatever this says. What it\ndecides is which of the two Summerbase shows where it shows one.\n","enum":["monthly","annual"],"type":"string"},"reporting_currency_code":{"description":"The currency every amount is reported in, by its ISO 4217 code.\nA project's first import sets it to the currency most of the imported\nsubscriptions are billed in, and nothing changes it afterwards but\nthis action.\n\nAmounts are converted at the rate for the moment being measured, so a\nproject billing in three currencies has one number to look at.\n","example":"USD","maxLength":3,"minLength":3,"type":"string"},"reporting_timezone":{"description":"The timezone every chart's buckets are cut in, by its IANA name:\n`Europe/Copenhagen`, `America/New_York`, `UTC`. An offset like `+02:00`\nand an abbreviation like `CET` are refused.\n\nIt decides which day a midnight-adjacent event falls in, and where a month\nstarts.\n","example":"America/New_York","type":"string"}},"type":"object"}}},"required":false},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Project"}}},"description":"Returns the updated project object."},"default":{"$ref":"#/components/responses/Error"}},"summary":"Update the current project","tags":["Projects"]}},"/v1/projects":{"get":{"callbacks":{},"deprecated":false,"description":"The projects in the organization you are working in. There is no\nlisting across organizations.\n\nSend `Summerbase-Organization` to name the organization.","operationId":"list_projects","parameters":[{"description":"How many records to return, 1 to 100. Defaults to 100.","in":"query","name":"limit","required":false,"schema":{"default":100,"maximum":100,"minimum":1,"type":"integer"}},{"description":"The `next_cursor` of the previous page, to read the page after it.","in":"query","name":"cursor","required":false,"schema":{"type":"string"}},{"description":"Whether to add `total_count`, how many records match altogether. On the first page only: it is the same number on every page of one list, so keep the first page's, and it is refused beside a `cursor`.","in":"query","name":"include_total_count","required":false,"schema":{"default":false,"type":"boolean"}},{"description":"Relationships to include in the response instead of fetching separately. A dotted path expands what an expanded record can expand in turn — at most 4 deep, 10 paths a request, and never back to a record already on the path.","in":"query","name":"expand[]","required":false,"schema":{"items":{"enum":["organization","reporting_currency"],"type":"string"},"maxItems":10,"type":"array"}},{"description":"The organization to work within. Optional for an API key, which is already scoped to one; naming a different one is refused.","in":"header","name":"Summerbase-Organization","required":false,"schema":{"format":"uuid","type":"string"}},{"description":"The project to work within. Its organization is implied, so this alone is enough. Optional for an API key, which is already scoped to one.","in":"header","name":"Summerbase-Project","required":false,"schema":{"format":"uuid","type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"additionalProperties":false,"description":"A page of results.","properties":{"data":{"items":{"$ref":"#/components/schemas/Project"},"type":"array"},"has_more":{"description":"Whether another page follows this one.","example":false,"type":"boolean"},"next_cursor":{"anyOf":[{"type":"null"},{"description":"Pass back as `cursor` to fetch the next page. Null on the last page.","type":"string"}]},"object":{"enum":["list"],"type":"string"},"total_count":{"description":"How many records match altogether, counting every page. Only present when `include_total_count=true` was sent, on the first page.","minimum":0,"type":"integer"}},"required":["object","data","has_more"],"type":"object"}}},"description":"Returns a list of the organization's projects."},"default":{"$ref":"#/components/responses/Error"}},"summary":"List projects","tags":["Projects"]},"post":{"callbacks":{},"deprecated":false,"description":"Create a project in the organization you are working in. Send\n`Summerbase-Organization`.\n\nA slug is derived from the name unless you name one. It is unique within\nthe organization, and one already used there is refused rather than\nchanged: name the project differently, or choose its slug.\n","operationId":"create_project","parameters":[{"description":"Relationships to include in the response instead of fetching separately. A dotted path expands what an expanded record can expand in turn — at most 4 deep, 10 paths a request, and never back to a record already on the path.","in":"query","name":"expand[]","required":false,"schema":{"items":{"enum":["organization","reporting_currency"],"type":"string"},"maxItems":10,"type":"array"}},{"description":"The organization to work within. Optional for an API key, which is already scoped to one; naming a different one is refused.","in":"header","name":"Summerbase-Organization","required":false,"schema":{"format":"uuid","type":"string"}},{"description":"The project to work within. Its organization is implied, so this alone is enough. Optional for an API key, which is already scoped to one.","in":"header","name":"Summerbase-Project","required":false,"schema":{"format":"uuid","type":"string"}}],"requestBody":{"content":{"application/json":{"example":{"name":"Staging"},"schema":{"additionalProperties":false,"properties":{"name":{"description":"What the project is called.","example":"Staging","maxLength":255,"minLength":1,"type":"string"},"slug":{"description":"How the project is addressed in a readable URL — the `production` in\n`app.summerbase.com/projects/production@acme`, or that one slug alone\nwhen it is the organization's too. Lowercase letters, digits, hyphens and\nunderscores, and never shaped like an id.\n\nUnique within the organization: one already used there is refused.\nDerived from the name when you do not choose one. Changing it is allowed\nand breaks every address that used the old one.\n","maxLength":100,"minLength":1,"type":"string"}},"required":["name"],"type":"object"}}},"required":true},"responses":{"201":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Project"}}},"description":"Returns the new project object."},"default":{"$ref":"#/components/responses/Error"}},"summary":"Create a project","tags":["Projects"]}},"/v1/project/slug":{"patch":{"callbacks":{},"deprecated":false,"description":"Move the project's slug, the address its pages in Summerbase are at.\n\nA slug is an address, not a name: renaming the project does not move\nit. Moving it changes every URL the project is reachable at, the old\none stops resolving immediately, and anything that stored it, such as a\nbookmark or a shared link, breaks. Nothing reserves the slug you leave\nbehind.\n","operationId":"move_project_slug","parameters":[{"description":"Relationships to include in the response instead of fetching separately. A dotted path expands what an expanded record can expand in turn — at most 4 deep, 10 paths a request, and never back to a record already on the path.","in":"query","name":"expand[]","required":false,"schema":{"items":{"enum":["organization","reporting_currency"],"type":"string"},"maxItems":10,"type":"array"}},{"description":"The organization to work within. Optional for an API key, which is already scoped to one; naming a different one is refused.","in":"header","name":"Summerbase-Organization","required":false,"schema":{"format":"uuid","type":"string"}},{"description":"The project to work within. Its organization is implied, so this alone is enough. Optional for an API key, which is already scoped to one.","in":"header","name":"Summerbase-Project","required":false,"schema":{"format":"uuid","type":"string"}}],"requestBody":{"content":{"application/json":{"example":{"slug":"production"},"schema":{"additionalProperties":false,"properties":{"slug":{"description":"How the project is addressed in a readable URL — the `production` in\n`app.summerbase.com/projects/production@acme`, or that one slug alone\nwhen it is the organization's too. Lowercase letters, digits, hyphens and\nunderscores, and never shaped like an id.\n\nUnique within the organization: one already used there is refused.\nDerived from the name when you do not choose one. Changing it is allowed\nand breaks every address that used the old one.\n","example":"production","maxLength":100,"minLength":1,"type":"string"}},"type":"object"}}},"required":false},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Project"}}},"description":"Returns the updated project object."},"default":{"$ref":"#/components/responses/Error"}},"summary":"Move the current project's slug","tags":["Projects"]}},"/v1/metrics":{"post":{"callbacks":{},"deprecated":false,"description":"Get any metric — MRR, ARR, churn, retention and the rest — as it is\nnow, as it changed over time, or split by plan, country or anything else\nyou know about your customers.\n\n## Quick start\n\n*Example: Your ARR now.*\n\n```http\nGET /v1/metrics?measure=arr\n```\n\n```http\n200 OK\n{\n  \"values\": {\"arr\": \"579000\"}\n}\n```\n\nEvery value is a decimal written as text, in the project's reporting\ncurrency. A percentage is already scaled: `\"2.1\"` means 2.1%. A value\nthat cannot be worked out, such as a growth rate from nothing, is `null`.\n\nThe answer also has `query` and `meta`, described under What comes\nback below. The examples here leave them out.\n\n## Getting a specific measure\n\nThese are the measures most people start with. Each one is a single\nrequest.\n\n### MRR: Monthly Run Rate\n\nRevenue per month from every active subscription, after discounts.\nIt counts recurring and usage revenue alike.\n\n```http\nGET /v1/metrics?measure=mrr\n```\n\n### ARR: Annual Run Rate\n\nThe same revenue per year: MRR times twelve.\n\n```http\nGET /v1/metrics?measure=arr\n```\n\n### Paying customers\n\nHow many customers have revenue.\n\n```http\nGET /v1/metrics?measure=paying_customers\n```\n\n### ARPA: Average Revenue Per Account\n\nWhat a paying customer pays per month on average: MRR divided by\npaying customers.\n\n```http\nGET /v1/metrics?measure=arpa_per_month\n```\n\n### MRR movement: what changed MRR\n\nHow much MRR went up or down this month so far. Add a breakdown by\n`movement_type`, under Breakdowns below, to split it into new,\nexpansion, reactivation, contraction and churn.\n\n```http\nGET /v1/metrics?measure=mrr_movement\n```\n\n### Customer churn\n\nThe share of paying customers lost over the last month, as a\npercentage.\n\n```http\nGET /v1/metrics?measure=customer_churn\n```\n\n### Revenue churn\n\nThe share of revenue lost to churn and contraction over the last\nmonth.\n\n```http\nGET /v1/metrics?measure=gross_revenue_churn\n```\n\n### Net revenue retention\n\nThe share of revenue kept over the last year, expansion included. It\nis above 100% when existing customers grew.\n\n```http\nGET /v1/metrics?measure=net_revenue_retention\n```\n\n### Every other measure\n\n[`GET /v1/metrics/measures`](/docs/api/operations/list-measures#all-measures) lists every measure, with what it means\nand how it is worked out:\nstarting levels, what a cohort lost and kept, signups and conversion,\nlifetime value and more.\n\n## Choosing measures\n\nEach number you can ask for is a **measure**, named by a key: `mrr`,\n`arr`, `paying_customers`, `customer_churn`.\n[`GET /v1/metrics/measures`](/docs/api/operations/list-measures#all-measures) lists them all, with what each one means\nand how it is worked out.\n\n`measure` takes one key, or a list of them. Each comes back under its\nown key.\n\n*Example: MRR, ARR, paying customers and average revenue per account, now.*\n\n```http\nGET /v1/metrics?measure[]=mrr&measure[]=arr&measure[]=paying_customers&measure[]=arpa_per_month\n```\n\n```http\n200 OK\n{\n  \"values\": {\n    \"mrr\": \"48250\",\n    \"arr\": \"579000\",\n    \"paying_customers\": \"193\",\n    \"arpa_per_month\": \"250\"\n  }\n}\n```\n\n## Over time\n\nAdd an `interval` to get one value per period in `timeseries`, oldest\nfirst. `values` stays the whole range: ARR at its end, or a flow such\nas MRR movement added up over all of it.\n\n*Example: ARR at the end of each month since July.*\n\n```http\nGET /v1/metrics?measure=arr&interval=month&from=now-3M/M\n```\n\n```http\n200 OK\n{\n  \"values\": {\"arr\": \"579000\"},\n  \"timeseries\": [\n    {\"period\": \"Jul 2026\", \"arr\": \"541200\"},\n    {\"period\": \"Aug 2026\", \"arr\": \"556200\"},\n    {\"period\": \"Sep 2026\", \"arr\": \"571200\"},\n    {\"period\": \"Oct 2026\", \"arr\": \"579000\"}\n  ]\n}\n```\n\nThe interval is `day`, `week`, `month`, `quarter` or `year`, on the\ncalendar of the project's reporting timezone.\n\n### Choosing the range\n\n`from` and `to` set the range. Each takes a date, `2026-01-01`, or a\ndate relative to today, in the same syntax as Grafana's time ranges:\n\n| Expression | Means |\n|---|---|\n| `now` | Today. |\n| `now-30d` | 30 days ago. `d` is days, `w` weeks, `M` months, `Q` quarters and `y` years. |\n| `now/M` | The start of this month in `from`, its end in `to`. `/d`, `/w`, `/Q` and `/y` work the same way. |\n| `now-3M/M` | The start of the month three months ago. |\n| `beginning` | In `from` only: when your first revenue moved, or the start of its period with an interval. |\n\n`to` is the end of the day it names, and is today unless you say\notherwise. Left out, `from` depends on the interval:\n\n| `interval` | `from` unless said |\n|---|---|\n| none | `now/M`, the start of this month |\n| `day` | `now-30d/d`, 30 days back |\n| `week` | `now-52w/w`, 52 weeks back |\n| `month` | `now-12M/M`, 12 months back |\n| `quarter` | `now-4Q/Q`, 4 quarters back |\n| `year` | `now-5y/y`, 5 years back |\n\nWith an interval, that is a run of whole periods plus the one in\nprogress.\n\n## One number for a range\n\n`values` is always the whole range, read as one period. Without an\n`interval`, that is the whole answer. Left out, the range is this month\nso far.\n\n*Example: How much MRR has moved this month so far.*\n\n```http\nGET /v1/metrics?measure=mrr_movement\n```\n\n*Example: New paying customers in the third quarter of 2026.*\n\n```http\nGET /v1/metrics?measure=new_paying_customers&from=2026-07-01&to=2026-09-30\n```\n\n## How a value is read\n\nWhat a value means depends on what kind of measure it is.\n[`GET /v1/metrics/measures`](/docs/api/operations/list-measures#all-measures) says which kind each measure is. For `values` the period is the whole\nrange; for each entry in `timeseries`, it is that entry's period.\n\n| Kind | Read | Examples |\n|---|---|---|\n| Level | at the end of the period | `mrr`, `arr`, `paying_customers` |\n| Flow | added up over the period | `mrr_movement`, `new_paying_customers`, `new_signups` |\n| Rate | over a window ending at the end of the period | `customer_churn`, `net_revenue_retention` |\n\nA level beginning with `starting_` is read at the start of the period\ninstead. An annual measure, such as `arr`, is its monthly one times\ntwelve.\n\n## Churn and retention\n\nA rate such as churn needs a span of time to be measured over: its\n**window**. Each rate has its own window, the one it is usually quoted\nover.\n\n| Measure | Window unless said |\n|---|---|\n| Churn, growth and conversion rates, such as `customer_churn` | One interval back from the end of each period, or a month with no interval. |\n| Retention rates, such as `net_revenue_retention` | A year back from the end of each period. |\n| Average revenue per account, levels and flows | The period itself. |\n\n[`GET /v1/metrics/measures`](/docs/api/operations/list-measures#all-measures) gives each measure's window as\n`default_lookback_window`.\n\n*Example: The share of paying customers lost over the last month.*\n\n```http\nGET /v1/metrics?measure=customer_churn\n```\n\n*Example: Churn for each month, each read over a full month back, so the month in progress compares with the others.*\n\n```http\nGET /v1/metrics?measure=customer_churn&interval=month\n```\n\n*Example: Net revenue retention over the last year.*\n\n```http\nGET /v1/metrics?measure=net_revenue_retention\n```\n\n### Setting the window\n\n`lookback_window` sets the window yourself:\n\n| `lookback_window` | Window |\n|---|---|\n| `period` | The period itself. For the month in progress, that is the month so far. |\n| `rolling_interval` | One interval back from the end of the period. A month with no interval. |\n| `30d`, `1M`, `1Q`, `1y` | That long, back from the end of the period. |\n\nA window shorter than the interval is refused, because part of each\nperiod would not be counted: `1M` cannot be read by the quarter.\n`30d` by the month is allowed. A window can be at most ten years.\n\nAll the measures in one query share a window: the window of the first\none in `measure`, unless `lookback_window` says otherwise. `query`\nsays which window was used. To read two rates over their own windows,\nsend two queries.\n\n## Breakdowns\n\nA `breakdown` splits each value into categories. The categories add up\nto the whole for every measure that adds up, such as MRR and the number\nof customers.\n\n*Example: ARR by plan.*\n\n```http\nPOST /v1/metrics\n{\n  \"measure\": \"arr\",\n  \"breakdown\": [\"subscription_item.plan_id\"]\n}\n```\n\n```http\n200 OK\n{\n  \"values\": {\"arr\": \"579000\"},\n  \"breakdown\": {\n    \"key\": \"subscription_item.plan_id\",\n    \"categories\": [\n      {\n        \"category\": \"0198f1a4-2e5f-7b80-a1c2-3d4e5f6a7b8c\",\n        \"label\": \"Growth\",\n        \"values\": {\"arr\": \"348000\"}\n      },\n      {\n        \"category\": \"0198f1a4-3b71-7c02-9d4e-6f8a1b2c3d4e\",\n        \"label\": \"Scale\",\n        \"values\": {\"arr\": \"186000\"}\n      },\n      {\n        \"category\": \"0198f1a4-4c82-7d13-8e5f-7a9b2c3d4e5f\",\n        \"label\": \"Starter\",\n        \"values\": {\"arr\": \"45000\"}\n      }\n    ]\n  }\n}\n```\n\nEach category has the value it stands for in `category`, here a plan's\nid, and a readable name in `label`. It also has its own `values`, its\nown `timeseries` when there is an interval, and its own `breakdown` when\nthere is another dimension, all in the same shape as the top of the\nanswer.\n\nA breakdown can be by any key a filter can use:\n`customer.country_code`, `subscription_item.plan_id`, or one of your own\nproperties. [`GET /v1/fields`](/docs/api/operations/list-fields) lists them all. Two more split revenue in ways of their own:\n\n| Key | Splits by |\n|---|---|\n| `movement_type` | new, expansion, reactivation, contraction and churn |\n| `revenue_type` | recurring and usage |\n\n*Example: What moved MRR this month, by kind of movement.*\n\n```http\nPOST /v1/metrics\n{\n  \"measure\": [\"mrr\", \"mrr_movement\"],\n  \"breakdown\": [\"movement_type\"]\n}\n```\n\n```http\n200 OK\n{\n  \"values\": {\"mrr\": \"48250\", \"mrr_movement\": \"650\"},\n  \"breakdown\": {\n    \"key\": \"movement_type\",\n    \"categories\": [\n      {\"category\": \"new\", \"label\": \"New\", \"values\": {\"mrr_movement\": \"900\"}},\n      {\"category\": \"expansion\", \"label\": \"Expansion\", \"values\": {\"mrr_movement\": \"400\"}},\n      {\"category\": \"reactivation\", \"label\": \"Reactivation\", \"values\": {\"mrr_movement\": \"150\"}},\n      {\"category\": \"churn\", \"label\": \"Churn\", \"values\": {\"mrr_movement\": \"-650\"}},\n      {\"category\": \"contraction\", \"label\": \"Contraction\", \"values\": {\"mrr_movement\": \"-150\"}}\n    ]\n  }\n}\n```\n\nA movement caused by exchange rates, or by a customer moving in or out\nof a filter, gets a category of its own when there is one.\n\n### More than one dimension\n\n`breakdown` takes up to three dimensions. Each one splits the categories\nof the one before.\n\n*Example: MRR by country, then by plan within each country.*\n\n```http\nPOST /v1/metrics\n{\n  \"measure\": \"mrr\",\n  \"breakdown\": [\"customer.country_code\", \"subscription_item.plan_id\"]\n}\n```\n\n### Shaping a dimension\n\nA dimension can be an object instead of a key, to say more about it:\n\n| Field | Does |\n|---|---|\n| `key` | What to split by. Required. |\n| `max_categories` | How many categories to keep, from 1 to 50, `Other` included. 10 unless said. The largest are kept, and the rest are added up as `Other`. |\n| `truncate` | For a date: one category per `week`, `month`, `quarter` or `year`. A date has to be truncated. |\n| `boundaries` | For a number: a list of up to 20 numbers, each starting a range. Or `\"auto\"`, to have ranges chosen from your values. |\n| `at` | When a customer's value is read, as in a filter: see [Filters](/docs/api/filters#when-a-value-is-read). Unless said, as it was in each period. |\n| `measure` | Which measures the categories carry. Those of the query unless said. |\n\n*Example: MRR per monthly cohort, by the month each customer first paid.*\n\n```http\nPOST /v1/metrics\n{\n  \"measure\": \"mrr\",\n  \"interval\": \"month\",\n  \"breakdown\": [{\"key\": \"customer.first_paying_at\", \"truncate\": \"month\"}]\n}\n```\n\n*Example: Customers per band of ARR.*\n\n```http\nPOST /v1/metrics\n{\n  \"measure\": \"paying_customers\",\n  \"breakdown\": [{\"key\": \"customer.arr\", \"boundaries\": [1000, 10000, 100000]}]\n}\n```\n\nA category with nothing in it over the whole range is left out, `Other`\nincluded. The categories of `movement_type`, `revenue_type` and\n`boundaries` are always all there.\n\n## Filters\n\nA `filter` counts only the customers, subscriptions or items it matches.\nFor everything a filter can say, with examples of nesting conditions\nand of conditions on subscriptions and items, see [Filters](/docs/api/filters).\n\n*Example: ARR of customers in the US.*\n\n```http\nPOST /v1/metrics\n{\n  \"measure\": \"arr\",\n  \"filter\": {\"key\": \"customer.country_code\", \"equals\": \"US\"}\n}\n```\n\n## Comparing with earlier dates\n\n`snapshots` reads the measures again at up to eight other dates, each as\nit stood at the end of that day.\n\n*Example: ARR now, and a year ago.*\n\n```http\nGET /v1/metrics?measure=arr&snapshots[]=now-1y\n```\n\n```http\n200 OK\n{\n  \"values\": {\"arr\": \"579000\"},\n  \"snapshots\": [\n    {\"label\": \"1 year ago\", \"at\": \"2025-10-21T04:00:00Z\", \"arr\": \"312000\"}\n  ]\n}\n```\n\nA flow or a rate in a snapshot is read over the window ending that day:\none interval long, or from the start of the range with no interval. A date\nrounded to the interval, such as `now-1M/M` by the month, ends where a\nperiod does, so it reads exactly what that period would.\n\n*Example: Last month against the same month a year before.*\n\n```http\nGET /v1/metrics?measure=mrr_movement&interval=month&snapshots[]=now-1M/M&snapshots[]=now-13M/M\n```\n\n## Currency, timezone and kinds of revenue\n\n| Input | Does |\n|---|---|\n| `currency_code` | Reports in another currency, by ISO 4217 code: `EUR`. The project's reporting currency unless said. |\n| `timezone` | Cuts days and periods in another timezone, by IANA name: `Europe/Copenhagen`. The project's reporting timezone unless said. |\n| `revenue_types` | Counts only `recurring` or only `usage` revenue. Both unless said, which is what MRR, the Monthly Run Rate, counts. |\n\n## What comes back\n\n| Field | Holds |\n|---|---|\n| `values` | Each measure's value for the whole range. |\n| `timeseries` | One entry per period, with each measure's value. Only with an interval. |\n| `snapshots` | Each measure at each date in `snapshots`. Only when asked for. |\n| `breakdown` | The categories, each with its own `values`, `timeseries` and `breakdown`. Only when asked for. |\n| `query` | The query as it was answered: what you sent, with every default filled in. Send it back to ask the same query again. |\n| `meta` | The range as it resolved, the measures with their names and units, and the periods. |\n\n## Limits\n\n- At most 500 periods: a year and more by the day is refused.\n- At most three dimensions in a breakdown, 2,000 categories in all, and\n  100,000 values: categories times periods and snapshots.\n- At most eight snapshots.\n\nA query over the limit is refused with an error saying which limit it hit\nand how to stay under it.\n\n## The customers behind a value\n\n[`POST /v1/metrics/drill_through/customers`](/docs/api/operations/drill-through-customers) takes the same query and points at one\nvalue in its answer, such as the expansion in June, and lists the\ncustomers it is made of.\n\n## GET or POST\n\nA query can be sent either way, and gets the same answer. The\nexamples above show a `GET` wherever one can be sent.\n\n- **GET**: the inputs go in the query string. Repeat a list input:\n  `measure[]=mrr&measure[]=arr`.\n- **POST**: the inputs go in a JSON body. A query with a `filter` or a\n  `breakdown` has to be a POST, since both are objects.\n\n*Example: Your ARR now, as a POST.*\n\n```http\nPOST /v1/metrics\n{\n  \"measure\": \"arr\"\n}\n```\n\nIf you are writing code against this endpoint, use POST. It takes every\ninput, so nothing needs rewriting when you add a breakdown or a filter.","operationId":"query_metrics","parameters":[{"description":"The organization to work within. Optional for an API key, which is already scoped to one; naming a different one is refused.","in":"header","name":"Summerbase-Organization","required":false,"schema":{"format":"uuid","type":"string"}},{"description":"The project to work within. Its organization is implied, so this alone is enough. Optional for an API key, which is already scoped to one.","in":"header","name":"Summerbase-Project","required":false,"schema":{"format":"uuid","type":"string"}}],"requestBody":{"content":{"application/json":{"example":{"measure":"arr","interval":"month","from":"now-3M/M"},"schema":{"additionalProperties":false,"properties":{"breakdown":{"description":"Up to three dimensions to split each value by, such as\n`[\"subscription_item.plan_id\"]`. Each is a key, or an object that\nsays more about it.\n","items":{"anyOf":[{"description":"A key: `customer.country_code`, `subscription_item.plan_id`, `movement_type`, `revenue_type`.","type":"string"},{"additionalProperties":false,"properties":{"at":{"description":"When a record's value is read: `window`, as things stood at the time over each window — a level by the value at its end, a movement by the value when the money moved, a cohort by the value at its start — unless said; `now`, so every period is split by the value held now; an instant relative to it, `now-3M`; a date, read as the end of that day in the reporting timezone; or a datetime, ISO 8601, UTC when it names no zone. The same `at` a filter comparison takes. Not for the run rate movements' own dimensions.","type":"string"},"boundaries":{"anyOf":[{"items":{"type":"number"},"maxItems":20,"minItems":1,"type":"array"},{"enum":["auto"],"type":"string"}],"description":"For a key that holds a number: up to 20 numbers, each above the one before and each starting a category — under the first, first to second, …, last and over. Or `auto`, and the query chooses them: up to five ranges holding about as many records each, at round numbers, from the values the records hold — which is how to ask for a distribution without knowing the values. The answer's `query` carries the numbers chosen, to send back for the same ranges; `auto` again follows the values."},"key":{"description":"The key, named as a filter names it.","type":"string"},"max_categories":{"description":"At most how many categories this dimension answers with, `Other` counted among them: the largest by the first measure carried at this depth in the last period are kept, and the rest are folded into `Other`, always last. 1 to 50, 10 unless said. A dimension with a fixed set of categories keeps them all; one over dates keeps every period up to 50, oldest first, folding older ones into `Before …`; one with `boundaries` has one per range.","maximum":50,"minimum":1,"type":"integer"},"measure":{"anyOf":[{"type":"string"},{"items":{"type":"string"},"minItems":1,"type":"array"}],"description":"Which measures the nodes along this dimension carry: a key, or a list of them. The root's `measure` unless said, less any this dimension cannot carry."},"truncate":{"description":"For a key that holds a datetime: the unit its values are cut to, in the reporting timezone. Each category is one such unit, and the categories come in order, newest kept and older folded into `Before …`.","enum":["week","month","quarter","year"],"type":"string"}},"required":["key"],"type":"object"}]},"maxItems":3,"type":"array"},"currency_code":{"description":"The currency to report in, by ISO 4217 code: `EUR`. The project's\nreporting currency unless said.\n","maxLength":3,"minLength":3,"type":"string"},"filter":{"allOf":[{"$ref":"#/components/schemas/FilterCondition"}],"description":"Counts only the customers, subscriptions or items the condition\nmatches. See [Filters](/docs/api/filters) for how to write one.\n"},"from":{"description":"The start of the range: a date, `2026-01-01`, or a date relative to\ntoday, `now-12M/M`, or `beginning`, when your first revenue moved.\nUnless said, the start of this month with no interval, and a run of\nwhole periods with one: twelve months back by the month.\n","example":"now-3M/M","type":"string"},"interval":{"description":"Cuts the range into periods for `timeseries`: `day`, `week`, `month`,\n`quarter` or `year`. Left out, the whole range is one period, and\nthere is no `timeseries`.\n","enum":["day","week","month","quarter","year"],"example":"month","type":"string"},"lookback_window":{"description":"How far back a rate looks from the end of each period: `period`,\n`rolling_interval`, or a length such as `30d`, `1M` or `1y`. Unless\nsaid, the window of the first measure in `measure`.\n","maxLength":16,"type":"string"},"measure":{"anyOf":[{"enum":["starting_mrr","new_mrr","expansion_mrr","reactivation_mrr","churned_mrr","contraction_mrr","exchange_rate_mrr","mrr_movement","run_rate_movers","mrr","mrr_growth","starting_arr","new_arr","expansion_arr","reactivation_arr","churned_arr","contraction_arr","exchange_rate_arr","arr_movement","arr","arr_growth","starting_mrr_lost","starting_mrr_gained","starting_mrr_retained","starting_arr_lost","starting_arr_gained","starting_arr_retained","gross_revenue_churn","net_revenue_churn","gross_revenue_retention","net_revenue_retention","arpa_per_month","arpa_per_year","starting_customers","net_new_customers","customers","starting_paying_customers","new_paying_customers","reactivated_paying_customers","churned_paying_customers","net_new_paying_customers","paying_customers","starting_paying_customers_lost","starting_paying_customers_retained","customer_churn","monthly_customer_churn","customer_retention","new_leads","new_signups","signups_converted_to_paid","signup_to_paid_conversion","leads_converted_to_paid","lead_to_paid_conversion","median_lead_to_paid_time","median_signup_to_paid_time","customer_lifespan","ltv"],"type":"string"},{"items":{"enum":["starting_mrr","new_mrr","expansion_mrr","reactivation_mrr","churned_mrr","contraction_mrr","exchange_rate_mrr","mrr_movement","run_rate_movers","mrr","mrr_growth","starting_arr","new_arr","expansion_arr","reactivation_arr","churned_arr","contraction_arr","exchange_rate_arr","arr_movement","arr","arr_growth","starting_mrr_lost","starting_mrr_gained","starting_mrr_retained","starting_arr_lost","starting_arr_gained","starting_arr_retained","gross_revenue_churn","net_revenue_churn","gross_revenue_retention","net_revenue_retention","arpa_per_month","arpa_per_year","starting_customers","net_new_customers","customers","starting_paying_customers","new_paying_customers","reactivated_paying_customers","churned_paying_customers","net_new_paying_customers","paying_customers","starting_paying_customers_lost","starting_paying_customers_retained","customer_churn","monthly_customer_churn","customer_retention","new_leads","new_signups","signups_converted_to_paid","signup_to_paid_conversion","leads_converted_to_paid","lead_to_paid_conversion","median_lead_to_paid_time","median_signup_to_paid_time","customer_lifespan","ltv"],"type":"string"},"minItems":1,"type":"array"}],"description":"The measures to get, by key: one, `\"mrr\"`, or a list, `[\"mrr\",\n\"arr\"]`. [`GET /v1/metrics/measures`](/docs/api/operations/list-measures) lists them all. The first one also\ndecides the window a rate is read over.\n","example":"arr"},"revenue_types":{"description":"The kinds of revenue to count: `recurring`, `usage`, or both, which\nis what MRR, the Monthly Run Rate, counts and the default.\n","items":{"enum":["recurring","usage"],"type":"string"},"type":"array"},"snapshots":{"description":"Up to eight more dates to read the measures at, each at the end of\nthat day: `now-1y`, `2026-01-01`.\n","items":{"type":"string"},"type":"array"},"timezone":{"description":"The timezone to cut days and periods in, by IANA name:\n`Europe/Copenhagen`. The project's reporting timezone unless said.\n","type":"string"},"to":{"description":"The end of the range, in the same forms as `from`. It includes the\nwhole day it names. Today unless said.\n","type":"string"}},"required":["measure"],"type":"object"}}},"required":true},"responses":{"200":{"content":{"application/json":{"example":{"values":{"arr":"579000"},"timeseries":[{"period":"Jul 2026","arr":"541200"},{"period":"Aug 2026","arr":"556200"},{"period":"Sep 2026","arr":"571200"},{"period":"Oct 2026","arr":"579000"}],"query":{"from":"now-3M/M","to":"now","interval":"month","measure":["arr"],"breakdown":[],"filter":null,"snapshots":[],"currency_code":"USD","timezone":"America/New_York","revenue_types":["recurring","usage"],"lookback_window":"period"},"meta":{"from":"2026-07-01T04:00:00Z","to":"2026-10-21T04:00:00Z","measures":[{"key":"arr","label":"ARR","unit":"money","description":"`arr` is the Annual Run Rate: revenue per year from every active subscription at the end of each window, after discounts, recurring and usage alike."}],"periods":[{"label":"Jul 2026","starts_at":"2026-07-01T04:00:00Z","ends_at":"2026-08-01T04:00:00Z"},{"label":"Aug 2026","starts_at":"2026-08-01T04:00:00Z","ends_at":"2026-09-01T04:00:00Z"},{"label":"Sep 2026","starts_at":"2026-09-01T04:00:00Z","ends_at":"2026-10-01T04:00:00Z"},{"label":"Oct 2026","starts_at":"2026-10-01T04:00:00Z","ends_at":"2026-11-01T04:00:00Z","provisional":true,"elapsed":"0.645161"}]}},"schema":{"$ref":"#/components/schemas/MetricQueryResult"}}},"description":"Returns each measure's value in `values` and, by an interval, for each period in `timeseries`, and the same again for each category of a breakdown."},"default":{"$ref":"#/components/responses/Error"}},"summary":"Query metrics","tags":["Metrics"],"x-analytics-request":true}},"/v1/metrics/measures":{"get":{"callbacks":{},"deprecated":false,"description":"Every measure [`POST /v1/metrics`](/docs/api/operations/query-metrics) can answer with, in the order a\ntable shows them: its key, what to call it, its unit, what it can be\nbroken down by, and the window it is read over. Ask for any number\nof them by key, in `measure`. Every measure comes back in one answer.\n\n## All measures\n\nEvery measure there is, by what it is about: its name, its key, which\nis what `measure` takes and what an answer carries its values under,\nwhat it is and its unit. An amount is in the reporting currency, and a\npercentage is already scaled, so `4.2` is 4.2%. Each value is read\nover its period, unless the measure says it is read over a rolling\ninterval — one interval back from each period's end — or a year: the\nwindow it is read over when it comes first in `measure` and the\nquery names none.\n\n### Run rate\n\nMRR and ARR as they opened, moved and closed, and how many customers moved them.\n\n| Measure | What it is |\n|---|---|\n| **Starting MRR**<br>`starting_mrr` | `mrr` as it stood at the start of each window.<br>Unit: money |\n| **New MRR**<br>`new_mrr` | The MRR added in each window by customers who started paying for the first time, whether or not they still pay at its end.<br>Unit: money |\n| **Expansion MRR**<br>`expansion_mrr` | The MRR added in each window by paying customers who came to pay more.<br>Unit: money |\n| **Reactivation MRR**<br>`reactivation_mrr` | The MRR added in each window by customers who started paying again after having churned.<br>Unit: money |\n| **Churned MRR**<br>`churned_mrr` | The MRR lost in each window to customers who stopped paying, as a positive amount, including those who started inside it. `starting_mrr_lost` counts only those paying at its start.<br>Unit: money |\n| **Contraction MRR**<br>`contraction_mrr` | The MRR lost in each window to customers who came to pay less and still pay something, as a positive amount.<br>Unit: money |\n| **Exchange Rate MRR**<br>`exchange_rate_mrr` | How much `mrr` changed in each window because exchange rates moved, with nobody paying differently in their own currency: positive when the currencies customers pay in strengthened against the reporting currency, and zero for a project whose customers all pay in it.<br>Unit: money |\n| **MRR Movement**<br>`mrr_movement` | How much `mrr` changed over each window: what was new, expanded and reactivated, minus what contracted and churned, plus what exchange rates moved it by. Formula: `new_mrr` plus `expansion_mrr` plus `reactivation_mrr` minus `contraction_mrr` minus `churned_mrr` plus `exchange_rate_mrr`, and under a filter or a breakdown, what entered the category less what left it.<br>Unit: money |\n| **Run Rate Movers**<br>`run_rate_movers` | The number of customers whose revenue changed over each window.<br>Unit: count |\n| **MRR**<br>`mrr` | The Monthly Run Rate: revenue per month from every active subscription at the end of each window, after discounts, recurring and usage alike.<br>Unit: money |\n| **MRR Growth**<br>`mrr_growth` | How much `mrr` grew or fell over each window, as a percentage. Formula: `mrr_movement` divided by `starting_mrr`. Read over a rolling interval unless the query says otherwise.<br>Unit: percent |\n| **Starting ARR**<br>`starting_arr` | `arr` as it stood at the start of each window.<br>Unit: money |\n| **New ARR**<br>`new_arr` | The ARR added in each window by customers who started paying for the first time, whether or not they still pay at its end.<br>Unit: money |\n| **Expansion ARR**<br>`expansion_arr` | The ARR added in each window by paying customers who came to pay more.<br>Unit: money |\n| **Reactivation ARR**<br>`reactivation_arr` | The ARR added in each window by customers who started paying again after having churned.<br>Unit: money |\n| **Churned ARR**<br>`churned_arr` | The ARR lost in each window to customers who stopped paying, as a positive amount, including those who started inside it. `starting_arr_lost` counts only those paying at its start.<br>Unit: money |\n| **Contraction ARR**<br>`contraction_arr` | The ARR lost in each window to customers who came to pay less and still pay something, as a positive amount.<br>Unit: money |\n| **Exchange Rate ARR**<br>`exchange_rate_arr` | How much `arr` changed in each window because exchange rates moved, with nobody paying differently in their own currency: positive when the currencies customers pay in strengthened against the reporting currency, and zero for a project whose customers all pay in it.<br>Unit: money |\n| **ARR Movement**<br>`arr_movement` | How much `arr` changed over each window: what was new, expanded and reactivated, minus what contracted and churned, plus what exchange rates moved it by. Formula: `new_arr` plus `expansion_arr` plus `reactivation_arr` minus `contraction_arr` minus `churned_arr` plus `exchange_rate_arr`, and under a filter or a breakdown, what entered the category less what left it.<br>Unit: money |\n| **ARR**<br>`arr` | The Annual Run Rate: revenue per year from every active subscription at the end of each window, after discounts, recurring and usage alike.<br>Unit: money |\n| **ARR Growth**<br>`arr_growth` | How much `arr` grew or fell over each window, as a percentage. Formula: `arr_movement` divided by `starting_arr`. Read over a rolling interval unless the query says otherwise.<br>Unit: percent |\n\n### Run rate of the starting customers\n\nWhat the customers paying at the window's start lost, gained and kept of their MRR and ARR.\n\n| Measure | What it is |\n|---|---|\n| **Starting MRR Lost**<br>`starting_mrr_lost` | How much less per month the customers paying at the start of each window pay at its end: churn and contraction.<br>Unit: money |\n| **Starting MRR Gained**<br>`starting_mrr_gained` | How much more per month the customers paying at the start of each window pay at its end: expansion.<br>Unit: money |\n| **Starting MRR Retained**<br>`starting_mrr_retained` | What the customers paying at the start of each window pay per month at its end, expansion included, so it exceeds `starting_mrr` when they grew.<br>Unit: money |\n| **Starting ARR Lost**<br>`starting_arr_lost` | How much less per year the customers paying at the start of each window pay at its end: churn and contraction.<br>Unit: money |\n| **Starting ARR Gained**<br>`starting_arr_gained` | How much more per year the customers paying at the start of each window pay at its end: expansion.<br>Unit: money |\n| **Starting ARR Retained**<br>`starting_arr_retained` | What the customers paying at the start of each window pay per year at its end, expansion included, so it exceeds `starting_arr` when they grew.<br>Unit: money |\n\n### Customers\n\nCustomers and paying customers as they opened, moved and closed, and who started and stopped paying.\n\n| Measure | What it is |\n|---|---|\n| **Starting Customers**<br>`starting_customers` | `customers` as it stood at the start of each window.<br>Unit: count |\n| **Net New Customers**<br>`net_new_customers` | How much `customers` changed over each window.<br>Unit: count |\n| **Customers**<br>`customers` | Every customer known at the end of each window, whatever their status: leads, free, trialing, paying and churned.<br>Unit: count |\n| **Starting Paying Customers**<br>`starting_paying_customers` | `paying_customers` as it stood at the start of each window.<br>Unit: count |\n| **New Paying Customers**<br>`new_paying_customers` | The number of customers who started paying for the first time in each window, whether or not they still pay at its end.<br>Unit: count |\n| **Reactivated Paying Customers**<br>`reactivated_paying_customers` | How many times customers started paying again in each window after having churned, whether or not they still pay at its end; a customer who came back twice counts twice.<br>Unit: count |\n| **Churned Paying Customers**<br>`churned_paying_customers` | How many times customers stopped paying in each window, including those who started inside it; a customer who churned twice counts twice. `starting_paying_customers_lost` counts only those paying at its start.<br>Unit: count |\n| **Net New Paying Customers**<br>`net_new_paying_customers` | How much `paying_customers` changed over each window, except that under a breakdown or a filter a customer who moved category is neither. Formula: `new_paying_customers` plus `reactivated_paying_customers` minus `churned_paying_customers`.<br>Unit: count |\n| **Paying Customers**<br>`paying_customers` | The number of customers with revenue at the end of each window.<br>Unit: count |\n\n### Starting customers\n\nHow many of the customers paying at the window's start were lost and kept.\n\n| Measure | What it is |\n|---|---|\n| **Starting Paying Customers Lost**<br>`starting_paying_customers_lost` | The number of customers with revenue at the start of each window who have none at its end.<br>Unit: count |\n| **Starting Paying Customers Retained**<br>`starting_paying_customers_retained` | The number of customers with revenue at both the start and the end of each window.<br>Unit: count |\n\n### Churn and retention\n\nThe share of the customers paying at the window's start, and of their revenue, lost and kept.\n\n| Measure | What it is |\n|---|---|\n| **Gross Revenue Churn**<br>`gross_revenue_churn` | The share of revenue lost to churn and contraction over each window. Formula: `starting_mrr_lost` divided by `starting_mrr`, taken at the exchange rate of each window's end rather than its start, as `starting_mrr_lost` is. Read over a rolling interval unless the query says otherwise.<br>Unit: percent |\n| **Net Revenue Churn**<br>`net_revenue_churn` | The share of revenue lost over each window after expansion is counted; negative when existing customers grew. Formula: `starting_mrr_lost` minus `starting_mrr_gained`, divided by `starting_mrr`, taken at the exchange rate of each window's end rather than its start, as the other two are. Read over a rolling interval unless the query says otherwise.<br>Unit: percent |\n| **Gross Revenue Retention**<br>`gross_revenue_retention` | The share of revenue kept over each window, before any expansion. Formula: `starting_mrr` minus `starting_mrr_lost`, divided by `starting_mrr`, with `starting_mrr` taken at the exchange rate of each window's end rather than its start, as `starting_mrr_lost` is. Read over a year unless the query says otherwise.<br>Unit: percent |\n| **Net Revenue Retention**<br>`net_revenue_retention` | The share of revenue kept over each window, expansion included; above 100% when existing customers grew. Formula: `starting_mrr_retained` divided by `starting_mrr`, taken at the exchange rate of each window's end rather than its start, as `starting_mrr_retained` is. Read over a year unless the query says otherwise.<br>Unit: percent |\n| **Customer Churn**<br>`customer_churn` | The share of paying customers lost over each window. Formula: `starting_paying_customers_lost` divided by `starting_paying_customers`. Read over a rolling interval unless the query says otherwise.<br>Unit: percent |\n| **Monthly Customer Churn**<br>`monthly_customer_churn` | `customer_churn` restated as a rate per month, whatever the length of each window. Read over a rolling interval unless the query says otherwise.<br>Unit: percent |\n| **Customer Retention**<br>`customer_retention` | The share of paying customers kept over each window. Formula: `starting_paying_customers_retained` divided by `starting_paying_customers`. Read over a year unless the query says otherwise.<br>Unit: percent |\n\n### Revenue per account\n\nWhat a paying customer pays on average.\n\n| Measure | What it is |\n|---|---|\n| **ARPA per Month**<br>`arpa_per_month` | The Average Revenue Per Account per month: what a paying customer pays on average at the end of each window. Formula: `mrr` divided by `paying_customers`.<br>Unit: money |\n| **ARPA per Year**<br>`arpa_per_year` | The Average Revenue Per Account per year: what a paying customer pays on average at the end of each window. Formula: `arr` divided by `paying_customers`.<br>Unit: money |\n\n### Funnel\n\nWho is new at each step from lead to paying, the share of a step that reached a later one, and how long it took.\n\n| Measure | What it is |\n|---|---|\n| **New Leads**<br>`new_leads` | The number of customers first seen in each window as leads.<br>Unit: count |\n| **New Signups**<br>`new_signups` | The number of customers who signed up in each window.<br>Unit: count |\n| **Signups Converted to Paid**<br>`signups_converted_to_paid` | The number of each window's `new_signups` who have become paying since. A recent window reads low and rises as its signups convert.<br>Unit: count |\n| **Signup-to-Paid Conversion**<br>`signup_to_paid_conversion` | The share of each window's `new_signups` who have become paying since. A recent window reads low and rises as its signups convert. Formula: `signups_converted_to_paid` divided by `new_signups`. Read over a rolling interval unless the query says otherwise.<br>Unit: percent |\n| **Leads Converted to Paid**<br>`leads_converted_to_paid` | The number of each window's `new_leads` who have become paying since. A recent window reads low and rises as its leads convert.<br>Unit: count |\n| **Lead-to-Paid Conversion**<br>`lead_to_paid_conversion` | The share of each window's `new_leads` who have become paying since. A recent window reads low and rises as its leads convert. Formula: `leads_converted_to_paid` divided by `new_leads`. Read over a rolling interval unless the query says otherwise.<br>Unit: percent |\n| **Median Lead-to-Paid Time**<br>`median_lead_to_paid_time` | The median number of days from first being seen as a lead to first paying, of the customers who became paying in each window. Read over a rolling interval unless the query says otherwise.<br>Unit: days |\n| **Median Signup-to-Paid Time**<br>`median_signup_to_paid_time` | The median number of days from signing up to first paying, of the customers who became paying in each window. Read over a rolling interval unless the query says otherwise.<br>Unit: days |\n\n### Lifetime\n\nHow long a paying customer stays, and what they are worth over it.\n\n| Measure | What it is |\n|---|---|\n| **Customer Lifespan**<br>`customer_lifespan` | How many months a paying customer stays on average, at each window's rate of churn. Formula: one divided by `monthly_customer_churn`. Read over a rolling interval unless the query says otherwise.<br>Unit: months |\n| **LTV**<br>`ltv` | The Lifetime Value: what a paying customer pays over the time they stay, before any cost. Formula: `arpa_per_month` times `customer_lifespan`. Read over a rolling interval unless the query says otherwise.<br>Unit: money |","operationId":"list_measures","parameters":[{"description":"How many records to return, 1 to 100. Defaults to 100.","in":"query","name":"limit","required":false,"schema":{"default":100,"maximum":100,"minimum":1,"type":"integer"}},{"description":"The `next_cursor` of the previous page, to read the page after it.","in":"query","name":"cursor","required":false,"schema":{"type":"string"}},{"description":"Whether to add `total_count`, how many records match altogether. On the first page only: it is the same number on every page of one list, so keep the first page's, and it is refused beside a `cursor`.","in":"query","name":"include_total_count","required":false,"schema":{"default":false,"type":"boolean"}},{"description":"The organization to work within. Optional for an API key, which is already scoped to one; naming a different one is refused.","in":"header","name":"Summerbase-Organization","required":false,"schema":{"format":"uuid","type":"string"}},{"description":"The project to work within. Its organization is implied, so this alone is enough. Optional for an API key, which is already scoped to one.","in":"header","name":"Summerbase-Project","required":false,"schema":{"format":"uuid","type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"additionalProperties":false,"description":"A page of results.","properties":{"data":{"items":{"$ref":"#/components/schemas/Measure"},"type":"array"},"has_more":{"description":"Whether another page follows this one.","example":false,"type":"boolean"},"next_cursor":{"anyOf":[{"type":"null"},{"description":"Pass back as `cursor` to fetch the next page. Null on the last page.","type":"string"}]},"object":{"enum":["list"],"type":"string"},"total_count":{"description":"How many records match altogether, counting every page. Only present when `include_total_count=true` was sent, on the first page.","minimum":0,"type":"integer"}},"required":["object","data","has_more"],"type":"object"}}},"description":"Returns a list of every measure a metrics query can ask for."},"default":{"$ref":"#/components/responses/Error"}},"summary":"List available measures","tags":["Metrics"]}},"/v1/fields":{"get":{"callbacks":{},"deprecated":false,"description":"Every key a filter's comparison or a breakdown's dimension may name in the\nproject you are working in: the fields a customer, a subscription and a\nsubscription item carry, the properties each source has written about them,\nand the run rate movements' own dimensions. Each says what it holds; which\na query takes is listed with the query, in a measure's `filter_keys` and\n`dimensions`. One answer holds a hundred fields; in a project with more,\nfind the one you need with `search` rather than reading on.\n","operationId":"list_fields","parameters":[{"description":"Only fields whose key or name contains this, ignoring case: `seats`\nfinds `customer.properties.backend.seats` and anything named Seats.\n","in":"query","name":"search","required":false,"schema":{"maxLength":255,"type":"string"}},{"description":"How many records to return, 1 to 100. Defaults to 100.","in":"query","name":"limit","required":false,"schema":{"default":100,"maximum":100,"minimum":1,"type":"integer"}},{"description":"The `next_cursor` of the previous page, to read the page after it.","in":"query","name":"cursor","required":false,"schema":{"type":"string"}},{"description":"Whether to add `total_count`, how many records match altogether. On the first page only: it is the same number on every page of one list, so keep the first page's, and it is refused beside a `cursor`.","in":"query","name":"include_total_count","required":false,"schema":{"default":false,"type":"boolean"}},{"description":"The organization to work within. Optional for an API key, which is already scoped to one; naming a different one is refused.","in":"header","name":"Summerbase-Organization","required":false,"schema":{"format":"uuid","type":"string"}},{"description":"The project to work within. Its organization is implied, so this alone is enough. Optional for an API key, which is already scoped to one.","in":"header","name":"Summerbase-Project","required":false,"schema":{"format":"uuid","type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"additionalProperties":false,"description":"A page of results.","properties":{"data":{"items":{"$ref":"#/components/schemas/Field"},"type":"array"},"has_more":{"description":"Whether another page follows this one.","example":false,"type":"boolean"},"next_cursor":{"anyOf":[{"type":"null"},{"description":"Pass back as `cursor` to fetch the next page. Null on the last page.","type":"string"}]},"object":{"enum":["list"],"type":"string"},"total_count":{"description":"How many records match altogether, counting every page. Only present when `include_total_count=true` was sent, on the first page.","minimum":0,"type":"integer"}},"required":["object","data","has_more"],"type":"object"}}},"description":"Returns a list of the fields a filter or a breakdown may name in the project."},"default":{"$ref":"#/components/responses/Error"}},"summary":"List fields","tags":["Fields"]}},"/v1/countries":{"get":{"callbacks":{},"deprecated":false,"description":"Every country, by name.","operationId":"list_countries","parameters":[{"description":"How many records to return, 1 to 100. Defaults to 20.","in":"query","name":"limit","required":false,"schema":{"default":20,"maximum":100,"minimum":1,"type":"integer"}},{"description":"The `next_cursor` of the previous page, to read the page after it.","in":"query","name":"cursor","required":false,"schema":{"type":"string"}},{"description":"Whether to add `total_count`, how many records match altogether. On the first page only: it is the same number on every page of one list, so keep the first page's, and it is refused beside a `cursor`.","in":"query","name":"include_total_count","required":false,"schema":{"default":false,"type":"boolean"}},{"description":"The organization to work within. Optional for an API key, which is already scoped to one; naming a different one is refused.","in":"header","name":"Summerbase-Organization","required":false,"schema":{"format":"uuid","type":"string"}},{"description":"The project to work within. Its organization is implied, so this alone is enough. Optional for an API key, which is already scoped to one.","in":"header","name":"Summerbase-Project","required":false,"schema":{"format":"uuid","type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"additionalProperties":false,"description":"A page of results.","properties":{"data":{"items":{"$ref":"#/components/schemas/Country"},"type":"array"},"has_more":{"description":"Whether another page follows this one.","example":false,"type":"boolean"},"next_cursor":{"anyOf":[{"type":"null"},{"description":"Pass back as `cursor` to fetch the next page. Null on the last page.","type":"string"}]},"object":{"enum":["list"],"type":"string"},"total_count":{"description":"How many records match altogether, counting every page. Only present when `include_total_count=true` was sent, on the first page.","minimum":0,"type":"integer"}},"required":["object","data","has_more"],"type":"object"}}},"description":"Returns a list of countries."},"default":{"$ref":"#/components/responses/Error"}},"summary":"List countries","tags":["Countries"]}},"/v1/currencies":{"get":{"callbacks":{},"deprecated":false,"description":"Every currency Summerbase holds rates for, by code.","operationId":"list_currencies","parameters":[{"description":"How many records to return, 1 to 100. Defaults to 20.","in":"query","name":"limit","required":false,"schema":{"default":20,"maximum":100,"minimum":1,"type":"integer"}},{"description":"The `next_cursor` of the previous page, to read the page after it.","in":"query","name":"cursor","required":false,"schema":{"type":"string"}},{"description":"Whether to add `total_count`, how many records match altogether. On the first page only: it is the same number on every page of one list, so keep the first page's, and it is refused beside a `cursor`.","in":"query","name":"include_total_count","required":false,"schema":{"default":false,"type":"boolean"}},{"description":"The organization to work within. Optional for an API key, which is already scoped to one; naming a different one is refused.","in":"header","name":"Summerbase-Organization","required":false,"schema":{"format":"uuid","type":"string"}},{"description":"The project to work within. Its organization is implied, so this alone is enough. Optional for an API key, which is already scoped to one.","in":"header","name":"Summerbase-Project","required":false,"schema":{"format":"uuid","type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"additionalProperties":false,"description":"A page of results.","properties":{"data":{"items":{"$ref":"#/components/schemas/Currency"},"type":"array"},"has_more":{"description":"Whether another page follows this one.","example":false,"type":"boolean"},"next_cursor":{"anyOf":[{"type":"null"},{"description":"Pass back as `cursor` to fetch the next page. Null on the last page.","type":"string"}]},"object":{"enum":["list"],"type":"string"},"total_count":{"description":"How many records match altogether, counting every page. Only present when `include_total_count=true` was sent, on the first page.","minimum":0,"type":"integer"}},"required":["object","data","has_more"],"type":"object"}}},"description":"Returns a list of currencies."},"default":{"$ref":"#/components/responses/Error"}},"summary":"List currencies","tags":["Currencies"]}},"/v1/timezones":{"get":{"callbacks":{},"deprecated":false,"description":"Every timezone a project or a metrics query can be read in, by name.","operationId":"list_timezones","parameters":[{"description":"How many records to return, 1 to 100. Defaults to 20.","in":"query","name":"limit","required":false,"schema":{"default":20,"maximum":100,"minimum":1,"type":"integer"}},{"description":"The `next_cursor` of the previous page, to read the page after it.","in":"query","name":"cursor","required":false,"schema":{"type":"string"}},{"description":"Whether to add `total_count`, how many records match altogether. On the first page only: it is the same number on every page of one list, so keep the first page's, and it is refused beside a `cursor`.","in":"query","name":"include_total_count","required":false,"schema":{"default":false,"type":"boolean"}},{"description":"The organization to work within. Optional for an API key, which is already scoped to one; naming a different one is refused.","in":"header","name":"Summerbase-Organization","required":false,"schema":{"format":"uuid","type":"string"}},{"description":"The project to work within. Its organization is implied, so this alone is enough. Optional for an API key, which is already scoped to one.","in":"header","name":"Summerbase-Project","required":false,"schema":{"format":"uuid","type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"additionalProperties":false,"description":"A page of results.","properties":{"data":{"items":{"$ref":"#/components/schemas/Timezone"},"type":"array"},"has_more":{"description":"Whether another page follows this one.","example":false,"type":"boolean"},"next_cursor":{"anyOf":[{"type":"null"},{"description":"Pass back as `cursor` to fetch the next page. Null on the last page.","type":"string"}]},"object":{"enum":["list"],"type":"string"},"total_count":{"description":"How many records match altogether, counting every page. Only present when `include_total_count=true` was sent, on the first page.","minimum":0,"type":"integer"}},"required":["object","data","has_more"],"type":"object"}}},"description":"Returns a list of timezones."},"default":{"$ref":"#/components/responses/Error"}},"summary":"List timezones","tags":["Timezones"]}},"/v1/metrics/drill_through/customers":{"post":{"callbacks":{},"deprecated":false,"description":"The customers behind one value of an answer of [`POST /v1/metrics`](/docs/api/operations/query-metrics):\nsend the query again as `query`, and point at the value with `value`.\n\n## Example\n\n### The query\n\nMRR movements by month since June, by type, answer with the\nexpansion in June among them:\n\n```http\nPOST /v1/metrics\n{\n  \"measure\": [\"mrr\", \"mrr_movement\"],\n  \"interval\": \"month\",\n  \"from\": \"2026-06-01\",\n  \"breakdown\": [\"movement_type\"]\n}\n```\n\n```http\n200 OK\n{\n  \"breakdown\": {\n    \"key\": \"movement_type\",\n    \"categories\": [\n      {\n        \"category\": \"expansion\",\n        \"label\": \"Expansion\",\n        \"values\": {\"mrr_movement\": \"980\"},\n        \"timeseries\": [\n          {\"period\": \"Jun 2026\", \"mrr_movement\": \"1450\"},\n          ...\n        ]\n      },\n      ...\n    ]\n  },\n  \"meta\": {\n    \"periods\": [\n      {\"label\": \"Jun 2026\", \"starts_at\": \"2026-06-01T04:00:00Z\", \"ends_at\": \"2026-07-01T04:00:00Z\"},\n      ...\n    ]\n  }\n}\n```\n\n### The customers behind it\n\nSend that query as `query` — or the answer's own `query`, the same\nquery with every default filled in — and point at the value with\n`value`: its `measure`; the `period` it is in, the `starts_at` of the\nperiod in `meta.periods`, or none for the whole range, the one `values` is for; and its\n`category_path`, the `category` of each node down the breakdown to\nit, none for a value at the root.\n\n```http\nPOST /v1/metrics/drill_through/customers\n{\n  \"query\": {\"measure\": [\"mrr\", \"mrr_movement\"], \"interval\": \"month\", \"from\": \"2026-06-01\", \"breakdown\": [\"movement_type\"]},\n  \"value\": {\"measure\": \"mrr_movement\", \"period\": \"2026-06-01T04:00:00Z\", \"category_path\": [\"expansion\"]}\n}\n```\n\nThe query's `measure` may be left out: the value's measure stands\nin, and which customers are listed does not depend on what else the\nanswer carried.\n\n## What comes back\n\nThe customers the value counts or sums: as many as a count\nsays, each with their share of an amount, `contribution`, which add up to it\n— less the exchange rate effect, which is nobody's — and what they\nwere worth at the window's start and its end. A rate lists whoever is\nbehind what it is over: a churn rate its lost customers, a conversion\nthose who converted. [`GET /v1/metrics/measures`](/docs/api/operations/list-measures) says which measures\nhave somebody to list, `drill_through_unit`; a starting run rate, a net of\ntwo counts, a lifespan and the exchange rate effect do not. Under a\nflow — what happened inside each period — the newest `moved_at`\nfirst, then the largest share; under anything else the largest share\nfirst, then the newest.\n\nEvery instant is a day in the project's reporting timezone, and every\namount is in its reporting currency unless `currency_code` says otherwise.\nThe period containing today is provisional: its numbers are still moving,\nand a `lookback_window` reads it over a window as long as every other.\nThe answer's `query` carries every input as it was used, defaults filled\nin, in the shape [`POST /v1/metrics`](/docs/api/operations/query-metrics) takes; its `meta` the measures\nthe answer carries and the periods it read.\n\n## The response\n\nThe example above answers:\n\n```http\n200 OK\n{\n  \"object\": \"list\",\n  \"data\": [\n    {\n      \"object\": \"metric_customer\",\n      \"customer_id\": \"0199a7c2-4e1b-7f38-b9d2-5a6c8e0f1d73\",\n      \"contribution\": \"250.00\",\n      \"starting_mrr\": \"1000.00\",\n      \"mrr\": \"1250.00\",\n      \"moved_at\": \"2026-06-12T15:47:09Z\",\n      \"status\": null\n    },\n    {\n      \"object\": \"metric_customer\",\n      \"customer_id\": \"0199a7c3-1d2e-7a4b-8c5d-6e7f8a9b0c1d\",\n      \"contribution\": \"1200.00\",\n      \"starting_mrr\": \"800.00\",\n      \"mrr\": \"2000.00\",\n      \"moved_at\": \"2026-06-03T09:20:41Z\",\n      \"status\": null\n    }\n  ],\n  \"has_more\": false,\n  \"next_cursor\": null\n}\n```\n\nExpand `customer` to get each one's record.","operationId":"drill_through_customers","parameters":[{"description":"The organization to work within. Optional for an API key, which is already scoped to one; naming a different one is refused.","in":"header","name":"Summerbase-Organization","required":false,"schema":{"format":"uuid","type":"string"}},{"description":"The project to work within. Its organization is implied, so this alone is enough. Optional for an API key, which is already scoped to one.","in":"header","name":"Summerbase-Project","required":false,"schema":{"format":"uuid","type":"string"}}],"requestBody":{"content":{"application/json":{"example":{"query":{"measure":["mrr","mrr_movement"],"interval":"month","from":"2026-06-01","breakdown":["movement_type"]},"value":{"measure":"mrr_movement","period":"2026-06-01T04:00:00Z","category_path":["expansion"]}},"schema":{"additionalProperties":false,"properties":{"cursor":{"description":"The `next_cursor` of the previous page, to read the page after it.","type":"string"},"expand":{"description":"Relationships to include in the response instead of fetching separately. A dotted path expands what an expanded record can expand in turn — at most 4 deep, 10 paths a request, and never back to a record already on the path.","items":{"enum":["customer","customer.country","customer.erasures","customer.subscriptions","customer.erasures.requested_by","customer.subscriptions.currency","customer.subscriptions.items","customer.subscriptions.owner_source","customer.subscriptions.items.price","customer.subscriptions.owner_source.current_import"],"type":"string"},"maxItems":10,"type":"array"},"include_total_count":{"default":false,"description":"Whether to add `total_count`, how many records match altogether. On the first page only: it is the same number on every page of one list, so keep the first page's, and it is refused beside a `cursor`.","type":"boolean"},"limit":{"default":20,"description":"How many records to return, 1 to 100. Defaults to 20.","maximum":100,"minimum":1,"type":"integer"},"query":{"allOf":[{"$ref":"#/components/schemas/MetricQuery"}],"description":"The metrics query the value came from: what was sent to\n[`POST /v1/metrics`](/docs/api/operations/query-metrics), or the answer's own `query`, which is the same\nquery with every default filled in.\n","example":{"breakdown":["movement_type"],"from":"2026-06-01","interval":"month","measure":["mrr","mrr_movement"]}},"value":{"additionalProperties":false,"description":"Which value of the answer: its `measure`, the `period` it is in, and\nthe `category_path` down the breakdown to it.\n","example":{"category_path":["expansion"],"measure":"mrr_movement","period":"2026-06-01T04:00:00Z"},"properties":{"category_path":{"anyOf":[{"type":"null"},{"items":{"anyOf":[{"type":"null"},{"type":"string"}]},"type":"array"}],"description":"The categories down the breakdown to the value's node, one per dimension, each as the node's `category` — `null` for `Other`. The root unless said."},"measure":{"description":"The measure the value is of, by the key [`GET /v1/metrics/measures`](/docs/api/operations/list-measures) lists it under.","type":"string"},"period":{"anyOf":[{"type":"null"},{"type":"string"}],"description":"The period the value is in: its `starts_at`, as the answer's `meta.periods` lists it. The whole range — the one `values` is for — unless said."}},"required":["measure"],"type":"object"}},"required":["value"],"type":"object"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"additionalProperties":false,"description":"A page of results.","properties":{"data":{"items":{"$ref":"#/components/schemas/MetricCustomer"},"type":"array"},"has_more":{"description":"Whether another page follows this one.","example":false,"type":"boolean"},"next_cursor":{"anyOf":[{"type":"null"},{"description":"Pass back as `cursor` to fetch the next page. Null on the last page.","type":"string"}]},"object":{"enum":["list"],"type":"string"},"total_count":{"description":"How many records match altogether, counting every page. Only present when `include_total_count=true` was sent, on the first page.","minimum":0,"type":"integer"}},"required":["object","data","has_more"],"type":"object"}}},"description":"Returns a list of the customers behind the value, each with their contribution to it."},"default":{"$ref":"#/components/responses/Error"}},"summary":"List the customers behind a value","tags":["Metrics"],"x-analytics-request":true}},"/v1/run_rate_movements":{"get":{"callbacks":{},"deprecated":false,"description":"Every change to revenue in the project, newest first — or one customer's,\nwith `customer_id`, or one subscription's, with `subscription_id`.\n\nOne row per change to a subscription item: they subscribed, they upgraded,\nthey moved plan, they left, they came back. Each row carries what the item\nwas on and worth before and after, so it explains itself. These are the same\nchanges the metrics report's movement values add up, so a month's movement\nand the rows inside it always agree.\n\nA customer's rows sharing a `counts_at` are one movement, because the\nproject's collapse window folds changes made close together into one: a\ncustomer who cancels one plan and takes another two days later has not\nchanged what they are worth, so both rows read `no_change`. Rows come\nordered by `counts_at`, then customer, then `occurred_at`, so a\nmovement's rows are next to each other; group by `counts_at` and\n`customer_id` to see movements, and read the rows to see what made them.\nA page boundary can fall inside a movement.\n\nAmounts are in the currency the customer is billed in. Expand `customer`,\n`price`, `previous_price`, `plan` and `previous_plan` to get them on each\nrow. A row's plans are its prices' as they stand now, so a price moved to\nanother plan reads under that plan as far back as it goes.\n","operationId":"list_run_rate_movements","parameters":[{"description":"Only this customer's. A Summerbase id, or any source's identity for\nthem like `stripe:cus_123`. A customer nobody has is a 404, not an\nempty list.\n","in":"query","name":"customer_id","required":false,"schema":{"type":"string"}},{"description":"Only this subscription's: the rows of changes to its items. A\nSummerbase id, or any source's identity for it like `stripe:sub_123`.\nA subscription nobody has is a 404, not an empty list. A row's\n`movement_type` is still its customer's movement, which may tell a\ndifferent story from this subscription's own `delta`.\n","in":"query","name":"subscription_id","required":false,"schema":{"type":"string"}},{"description":"Only rows of movements of this type.","in":"query","name":"movement_type","required":false,"schema":{"enum":["new","reactivation","expansion","contraction","churn","no_change"],"type":"string"}},{"description":"How many records to return, 1 to 100. Defaults to 20.","in":"query","name":"limit","required":false,"schema":{"default":20,"maximum":100,"minimum":1,"type":"integer"}},{"description":"The `next_cursor` of the previous page, to read the page after it.","in":"query","name":"cursor","required":false,"schema":{"type":"string"}},{"description":"Relationships to include in the response instead of fetching separately. A dotted path expands what an expanded record can expand in turn — at most 4 deep, 10 paths a request, and never back to a record already on the path.","in":"query","name":"expand[]","required":false,"schema":{"items":{"enum":["customer","plan","previous_plan","previous_price","price","customer.country","customer.erasures","customer.subscriptions","plan.prices","previous_plan.prices","previous_price.owner_source","previous_price.plan","price.owner_source","price.plan","customer.erasures.requested_by","customer.subscriptions.currency","customer.subscriptions.items","customer.subscriptions.owner_source","plan.prices.owner_source","previous_plan.prices.owner_source","previous_price.owner_source.current_import","price.owner_source.current_import","customer.subscriptions.items.price","customer.subscriptions.owner_source.current_import","plan.prices.owner_source.current_import","previous_plan.prices.owner_source.current_import"],"type":"string"},"maxItems":10,"type":"array"}},{"description":"The organization to work within. Optional for an API key, which is already scoped to one; naming a different one is refused.","in":"header","name":"Summerbase-Organization","required":false,"schema":{"format":"uuid","type":"string"}},{"description":"The project to work within. Its organization is implied, so this alone is enough. Optional for an API key, which is already scoped to one.","in":"header","name":"Summerbase-Project","required":false,"schema":{"format":"uuid","type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"additionalProperties":false,"description":"A page of results.","properties":{"data":{"items":{"$ref":"#/components/schemas/RunRateMovement"},"type":"array"},"has_more":{"description":"Whether another page follows this one.","example":false,"type":"boolean"},"next_cursor":{"anyOf":[{"type":"null"},{"description":"Pass back as `cursor` to fetch the next page. Null on the last page.","type":"string"}]},"object":{"enum":["list"],"type":"string"}},"required":["object","data","has_more"],"type":"object"}}},"description":"Returns a list of run rate movements, newest first."},"default":{"$ref":"#/components/responses/Error"}},"summary":"List run rate movements","tags":["Run rate movements"],"x-analytics-request":true}},"/v1/organization":{"get":{"callbacks":{},"deprecated":false,"description":"Fetch the organization the request is working in — the one named by\n`Summerbase-Organization`, or the one an API key's project belongs to.\n","operationId":"get_current_organization","parameters":[{"description":"The organization to work within. Optional for an API key, which is already scoped to one; naming a different one is refused.","in":"header","name":"Summerbase-Organization","required":false,"schema":{"format":"uuid","type":"string"}},{"description":"The project to work within. Its organization is implied, so this alone is enough. Optional for an API key, which is already scoped to one.","in":"header","name":"Summerbase-Project","required":false,"schema":{"format":"uuid","type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Organization"}}},"description":"Returns the organization object for the organization the request works in."},"default":{"$ref":"#/components/responses/Error"}},"summary":"Get the current organization","tags":["Organizations"]},"patch":{"callbacks":{},"deprecated":false,"description":"Rename the organization, or move its slug.\n\nRenaming does not move the slug: the two are separate, and a slug is an\naddress. Moving it changes every URL the organization is reachable at, and\nthe old one stops resolving immediately — anything that stored it, a\nbookmark or a script sending `Summerbase-Organization`, breaks. Nothing\nreserves the slug you leave behind.\n","operationId":"update_organization","parameters":[{"description":"The organization to work within. Optional for an API key, which is already scoped to one; naming a different one is refused.","in":"header","name":"Summerbase-Organization","required":false,"schema":{"format":"uuid","type":"string"}},{"description":"The project to work within. Its organization is implied, so this alone is enough. Optional for an API key, which is already scoped to one.","in":"header","name":"Summerbase-Project","required":false,"schema":{"format":"uuid","type":"string"}}],"requestBody":{"content":{"application/json":{"example":{"name":"Acme Analytics"},"schema":{"additionalProperties":false,"properties":{"name":{"description":"The company's name, as it appears throughout Summerbase.","example":"Acme Analytics","maxLength":255,"minLength":1,"type":"string"},"slug":{"description":"How the organization is addressed in a readable URL — the `acme` in\n`app.summerbase.com/organizations/acme`, and in its projects'\n`app.summerbase.com/projects/production@acme`. Lowercase letters, digits,\nhyphens and underscores, and never shaped like an id.\n\nUnique across every Summerbase organization, and derived from the name\nwhen you do not choose one. Changing it is allowed and breaks every\naddress that used the old one.\n","maxLength":100,"minLength":1,"type":"string"}},"type":"object"}}},"required":false},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Organization"}}},"description":"Returns the updated organization object."},"default":{"$ref":"#/components/responses/Error"}},"summary":"Update the current organization","tags":["Organizations"]}},"/v1/organizations":{"post":{"callbacks":{},"deprecated":false,"description":"Create an organization. Whoever creates it is its owner, and it starts\nwith one project carrying the organization's name, so there is somewhere\nto work.\n\nA slug is derived from the name unless you name one.\n\nIts data is kept in `region_code` and is never moved.\n","operationId":"create_organization","parameters":[{"description":"The organization to work within. Optional for an API key, which is already scoped to one; naming a different one is refused.","in":"header","name":"Summerbase-Organization","required":false,"schema":{"format":"uuid","type":"string"}},{"description":"The project to work within. Its organization is implied, so this alone is enough. Optional for an API key, which is already scoped to one.","in":"header","name":"Summerbase-Project","required":false,"schema":{"format":"uuid","type":"string"}}],"requestBody":{"content":{"application/json":{"example":{"accept_terms":true,"name":"Acme Analytics","region_code":"us"},"schema":{"additionalProperties":false,"properties":{"accept_terms":{"description":"That the person creating the organization accepts the Terms of Service\nand the Privacy Policy on its behalf. Anything but `true` is refused.\n","example":true,"type":"boolean"},"name":{"description":"The company's name, as it appears throughout Summerbase.","example":"Acme Analytics","maxLength":255,"minLength":1,"type":"string"},"region_code":{"description":"Where the organization's data is kept: `eu` or `us`. The request is\nrefused if that region is not open to new organizations.\n","enum":["eu","us"],"example":"us","type":"string"},"slug":{"description":"How the organization is addressed in a readable URL — the `acme` in\n`app.summerbase.com/organizations/acme`, and in its projects'\n`app.summerbase.com/projects/production@acme`. Lowercase letters, digits,\nhyphens and underscores, and never shaped like an id.\n\nUnique across every Summerbase organization, and derived from the name\nwhen you do not choose one. Changing it is allowed and breaks every\naddress that used the old one.\n","maxLength":100,"minLength":1,"type":"string"}},"required":["accept_terms","name","region_code"],"type":"object"}}},"required":true},"responses":{"201":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Organization"}}},"description":"Returns the new organization object."},"default":{"$ref":"#/components/responses/Error"}},"summary":"Create an organization","tags":["Organizations"]}},"/v1/me/memberships":{"get":{"callbacks":{},"deprecated":false,"description":"Every organization you belong to, and what you may do in each. Expand\n`organization` to get the companies themselves in the same call.\n","operationId":"list_my_memberships","parameters":[{"description":"How many records to return, 1 to 100. Defaults to 20.","in":"query","name":"limit","required":false,"schema":{"default":20,"maximum":100,"minimum":1,"type":"integer"}},{"description":"The `next_cursor` of the previous page, to read the page after it.","in":"query","name":"cursor","required":false,"schema":{"type":"string"}},{"description":"Whether to add `total_count`, how many records match altogether. On the first page only: it is the same number on every page of one list, so keep the first page's, and it is refused beside a `cursor`.","in":"query","name":"include_total_count","required":false,"schema":{"default":false,"type":"boolean"}},{"description":"Relationships to include in the response instead of fetching separately. A dotted path expands what an expanded record can expand in turn — at most 4 deep, 10 paths a request, and never back to a record already on the path.","in":"query","name":"expand[]","required":false,"schema":{"items":{"enum":["organization","user"],"type":"string"},"maxItems":10,"type":"array"}},{"description":"The organization to work within. Optional for an API key, which is already scoped to one; naming a different one is refused.","in":"header","name":"Summerbase-Organization","required":false,"schema":{"format":"uuid","type":"string"}},{"description":"The project to work within. Its organization is implied, so this alone is enough. Optional for an API key, which is already scoped to one.","in":"header","name":"Summerbase-Project","required":false,"schema":{"format":"uuid","type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"additionalProperties":false,"description":"A page of results.","properties":{"data":{"items":{"$ref":"#/components/schemas/Membership"},"type":"array"},"has_more":{"description":"Whether another page follows this one.","example":false,"type":"boolean"},"next_cursor":{"anyOf":[{"type":"null"},{"description":"Pass back as `cursor` to fetch the next page. Null on the last page.","type":"string"}]},"object":{"enum":["list"],"type":"string"},"total_count":{"description":"How many records match altogether, counting every page. Only present when `include_total_count=true` was sent, on the first page.","minimum":0,"type":"integer"}},"required":["object","data","has_more"],"type":"object"}}},"description":"Returns a list of the memberships of the person the request is made as."},"default":{"$ref":"#/components/responses/Error"}},"summary":"List your memberships","tags":["Memberships"]}},"/v1/organization/memberships":{"get":{"callbacks":{},"deprecated":false,"description":"Everybody in this organization and what each of them may do, oldest first.\n","operationId":"list_organization_memberships","parameters":[{"description":"How many records to return, 1 to 100. Defaults to 20.","in":"query","name":"limit","required":false,"schema":{"default":20,"maximum":100,"minimum":1,"type":"integer"}},{"description":"The `next_cursor` of the previous page, to read the page after it.","in":"query","name":"cursor","required":false,"schema":{"type":"string"}},{"description":"Whether to add `total_count`, how many records match altogether. On the first page only: it is the same number on every page of one list, so keep the first page's, and it is refused beside a `cursor`.","in":"query","name":"include_total_count","required":false,"schema":{"default":false,"type":"boolean"}},{"description":"Relationships to include in the response instead of fetching separately. A dotted path expands what an expanded record can expand in turn — at most 4 deep, 10 paths a request, and never back to a record already on the path.","in":"query","name":"expand[]","required":false,"schema":{"items":{"enum":["organization","user"],"type":"string"},"maxItems":10,"type":"array"}},{"description":"The organization to work within. Optional for an API key, which is already scoped to one; naming a different one is refused.","in":"header","name":"Summerbase-Organization","required":false,"schema":{"format":"uuid","type":"string"}},{"description":"The project to work within. Its organization is implied, so this alone is enough. Optional for an API key, which is already scoped to one.","in":"header","name":"Summerbase-Project","required":false,"schema":{"format":"uuid","type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"additionalProperties":false,"description":"A page of results.","properties":{"data":{"items":{"$ref":"#/components/schemas/Membership"},"type":"array"},"has_more":{"description":"Whether another page follows this one.","example":false,"type":"boolean"},"next_cursor":{"anyOf":[{"type":"null"},{"description":"Pass back as `cursor` to fetch the next page. Null on the last page.","type":"string"}]},"object":{"enum":["list"],"type":"string"},"total_count":{"description":"How many records match altogether, counting every page. Only present when `include_total_count=true` was sent, on the first page.","minimum":0,"type":"integer"}},"required":["object","data","has_more"],"type":"object"}}},"description":"Returns a list of the organization's memberships."},"default":{"$ref":"#/components/responses/Error"}},"summary":"List the organization's memberships","tags":["Memberships"]}},"/v1/memberships/{id}":{"delete":{"callbacks":{},"deprecated":false,"description":"Remove a member from the organization. They keep their account.","operationId":"remove_membership","parameters":[{"example":"0198f1a2-6c40-7a9e-8d1b-3f5e7c9a2b64","in":"path","name":"id","required":true,"schema":{"format":"uuid","type":"string"}},{"description":"The organization to work within. Optional for an API key, which is already scoped to one; naming a different one is refused.","in":"header","name":"Summerbase-Organization","required":false,"schema":{"format":"uuid","type":"string"}},{"description":"The project to work within. Its organization is implied, so this alone is enough. Optional for an API key, which is already scoped to one.","in":"header","name":"Summerbase-Project","required":false,"schema":{"format":"uuid","type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"additionalProperties":false,"properties":{"deleted":{"enum":[true],"type":"boolean"},"id":{"format":"uuid","type":"string"},"object":{"enum":["membership"],"type":"string"}},"required":["id","object","deleted"],"type":"object"}}},"description":"Returns the removed membership's id."},"default":{"$ref":"#/components/responses/Error"}},"summary":"Remove a member","tags":["Memberships"]},"patch":{"callbacks":{},"deprecated":false,"description":"Change what a member may do in this organization.","operationId":"update_membership_role","parameters":[{"example":"0198f1a2-6c40-7a9e-8d1b-3f5e7c9a2b64","in":"path","name":"id","required":true,"schema":{"format":"uuid","type":"string"}},{"description":"Relationships to include in the response instead of fetching separately. A dotted path expands what an expanded record can expand in turn — at most 4 deep, 10 paths a request, and never back to a record already on the path.","in":"query","name":"expand[]","required":false,"schema":{"items":{"enum":["organization","user"],"type":"string"},"maxItems":10,"type":"array"}},{"description":"The organization to work within. Optional for an API key, which is already scoped to one; naming a different one is refused.","in":"header","name":"Summerbase-Organization","required":false,"schema":{"format":"uuid","type":"string"}},{"description":"The project to work within. Its organization is implied, so this alone is enough. Optional for an API key, which is already scoped to one.","in":"header","name":"Summerbase-Project","required":false,"schema":{"format":"uuid","type":"string"}}],"requestBody":{"content":{"application/json":{"example":{"role":"writer"},"schema":{"additionalProperties":false,"properties":{"role":{"description":"What the user may do in the organization, and in every project under it.\n","enum":["guest","viewer","writer","admin","owner"],"example":"writer","type":"string"}},"type":"object"}}},"required":false},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Membership"}}},"description":"Returns the updated membership object."},"default":{"$ref":"#/components/responses/Error"}},"summary":"Update a membership","tags":["Memberships"]}},"/v1/invitations":{"get":{"callbacks":{},"deprecated":false,"description":"Every invitation this organization has outstanding, oldest first. One that\nis past `expires_at` stays listed until it is cancelled or reissued.\n","operationId":"list_invitations","parameters":[{"description":"How many records to return, 1 to 100. Defaults to 20.","in":"query","name":"limit","required":false,"schema":{"default":20,"maximum":100,"minimum":1,"type":"integer"}},{"description":"The `next_cursor` of the previous page, to read the page after it.","in":"query","name":"cursor","required":false,"schema":{"type":"string"}},{"description":"Whether to add `total_count`, how many records match altogether. On the first page only: it is the same number on every page of one list, so keep the first page's, and it is refused beside a `cursor`.","in":"query","name":"include_total_count","required":false,"schema":{"default":false,"type":"boolean"}},{"description":"The organization to work within. Optional for an API key, which is already scoped to one; naming a different one is refused.","in":"header","name":"Summerbase-Organization","required":false,"schema":{"format":"uuid","type":"string"}},{"description":"The project to work within. Its organization is implied, so this alone is enough. Optional for an API key, which is already scoped to one.","in":"header","name":"Summerbase-Project","required":false,"schema":{"format":"uuid","type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"additionalProperties":false,"description":"A page of results.","properties":{"data":{"items":{"$ref":"#/components/schemas/Invitation"},"type":"array"},"has_more":{"description":"Whether another page follows this one.","example":false,"type":"boolean"},"next_cursor":{"anyOf":[{"type":"null"},{"description":"Pass back as `cursor` to fetch the next page. Null on the last page.","type":"string"}]},"object":{"enum":["list"],"type":"string"},"total_count":{"description":"How many records match altogether, counting every page. Only present when `include_total_count=true` was sent, on the first page.","minimum":0,"type":"integer"}},"required":["object","data","has_more"],"type":"object"}}},"description":"Returns a list of the organization's outstanding invitations."},"default":{"$ref":"#/components/responses/Error"}},"summary":"List invitations","tags":["Invitations"]},"post":{"callbacks":{},"deprecated":false,"description":"Invite an email address to join this organization, and send them a link.\n\nOne outstanding invitation per address: inviting somebody who already has\none is refused. Use [`POST /v1/invitations/{id}/resend`](/docs/api/operations/resend-invitation) to send the existing link\nagain.\n","operationId":"create_invitation","parameters":[{"description":"The organization to work within. Optional for an API key, which is already scoped to one; naming a different one is refused.","in":"header","name":"Summerbase-Organization","required":false,"schema":{"format":"uuid","type":"string"}},{"description":"The project to work within. Its organization is implied, so this alone is enough. Optional for an API key, which is already scoped to one.","in":"header","name":"Summerbase-Project","required":false,"schema":{"format":"uuid","type":"string"}}],"requestBody":{"content":{"application/json":{"example":{"email":"sam@example.com","role":"writer"},"schema":{"additionalProperties":false,"properties":{"email":{"description":"The address the invitation was sent to.","example":"sam@example.com","maxLength":255,"type":"string"},"role":{"description":"The role accepting the invitation grants: `viewer` reads everything,\n`writer` also changes data, and `admin` also manages integrations, API\nkeys and members.\n","enum":["guest","viewer","writer","admin","owner"],"example":"writer","type":"string"}},"required":["email"],"type":"object"}}},"required":true},"responses":{"201":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Invitation"}}},"description":"Returns the new invitation object."},"default":{"$ref":"#/components/responses/Error"}},"summary":"Create an invitation","tags":["Invitations"]}},"/v1/invitations/{id}/resend":{"post":{"callbacks":{},"deprecated":false,"description":"Send an invitation again, and give it another seven days from now.\n\nIt keeps the same link.\n","operationId":"resend_invitation","parameters":[{"example":"0199c3e4-2a7b-7f10-9c8d-4e6f1a3b5d27","in":"path","name":"id","required":true,"schema":{"format":"uuid","type":"string"}},{"description":"The organization to work within. Optional for an API key, which is already scoped to one; naming a different one is refused.","in":"header","name":"Summerbase-Organization","required":false,"schema":{"format":"uuid","type":"string"}},{"description":"The project to work within. Its organization is implied, so this alone is enough. Optional for an API key, which is already scoped to one.","in":"header","name":"Summerbase-Project","required":false,"schema":{"format":"uuid","type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"additionalProperties":false,"properties":{},"type":"object"}}},"required":false},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Invitation"}}},"description":"Returns the invitation object, with its new `expires_at`."},"default":{"$ref":"#/components/responses/Error"}},"summary":"Resend an invitation","tags":["Invitations"]}},"/v1/invitations/{id}":{"delete":{"callbacks":{},"deprecated":false,"description":"Cancel an invitation. Its link stops working immediately.","operationId":"cancel_invitation","parameters":[{"example":"0199c3e4-2a7b-7f10-9c8d-4e6f1a3b5d27","in":"path","name":"id","required":true,"schema":{"format":"uuid","type":"string"}},{"description":"The organization to work within. Optional for an API key, which is already scoped to one; naming a different one is refused.","in":"header","name":"Summerbase-Organization","required":false,"schema":{"format":"uuid","type":"string"}},{"description":"The project to work within. Its organization is implied, so this alone is enough. Optional for an API key, which is already scoped to one.","in":"header","name":"Summerbase-Project","required":false,"schema":{"format":"uuid","type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"additionalProperties":false,"properties":{"deleted":{"enum":[true],"type":"boolean"},"id":{"format":"uuid","type":"string"},"object":{"enum":["invitation"],"type":"string"}},"required":["id","object","deleted"],"type":"object"}}},"description":"Returns the canceled invitation's id."},"default":{"$ref":"#/components/responses/Error"}},"summary":"Cancel an invitation","tags":["Invitations"]}},"/v1/api_key":{"get":{"callbacks":{},"deprecated":false,"description":"Fetch the API key the request is made with. A request signed in as a\nperson has none, and is answered as not found.\n","operationId":"get_current_api_key","parameters":[{"description":"Relationships to include in the response instead of fetching separately. A dotted path expands what an expanded record can expand in turn — at most 4 deep, 10 paths a request, and never back to a record already on the path.","in":"query","name":"expand[]","required":false,"schema":{"items":{"enum":["source","source.current_import"],"type":"string"},"maxItems":10,"type":"array"}},{"description":"The organization to work within. Optional for an API key, which is already scoped to one; naming a different one is refused.","in":"header","name":"Summerbase-Organization","required":false,"schema":{"format":"uuid","type":"string"}},{"description":"The project to work within. Its organization is implied, so this alone is enough. Optional for an API key, which is already scoped to one.","in":"header","name":"Summerbase-Project","required":false,"schema":{"format":"uuid","type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiKey"}}},"description":"Returns the API key object for the key the request is made with."},"default":{"$ref":"#/components/responses/Error"}},"summary":"Get the current API key","tags":["API keys"]}},"/v1/integrations":{"get":{"callbacks":{},"deprecated":false,"description":"The integrations of the project you are working in. Send `Summerbase-Project`;\nthere is no listing across projects.\n","operationId":"list_integrations","parameters":[{"description":"The order.","in":"query","name":"sort","required":false,"schema":{"description":"The order: a key, `-` before it for descending, commas between up to 3 keys — `-created_at`, `name`, `-mrr,name`. The keys are `created_at`, `name`, `slug`, `priority`. Text sorts ignoring case; a record with no value for the key comes last, whichever way the list runs.","type":"string"}},{"description":"How many records to return, 1 to 100. Defaults to 20.","in":"query","name":"limit","required":false,"schema":{"default":20,"maximum":100,"minimum":1,"type":"integer"}},{"description":"The `next_cursor` of the previous page, to read the page after it.","in":"query","name":"cursor","required":false,"schema":{"type":"string"}},{"description":"Whether to add `total_count`, how many records match altogether. On the first page only: it is the same number on every page of one list, so keep the first page's, and it is refused beside a `cursor`.","in":"query","name":"include_total_count","required":false,"schema":{"default":false,"type":"boolean"}},{"description":"Relationships to include in the response instead of fetching separately. A dotted path expands what an expanded record can expand in turn — at most 4 deep, 10 paths a request, and never back to a record already on the path.","in":"query","name":"expand[]","required":false,"schema":{"items":{"enum":["current_import"],"type":"string"},"maxItems":10,"type":"array"}},{"description":"The organization to work within. Optional for an API key, which is already scoped to one; naming a different one is refused.","in":"header","name":"Summerbase-Organization","required":false,"schema":{"format":"uuid","type":"string"}},{"description":"The project to work within. Its organization is implied, so this alone is enough. Optional for an API key, which is already scoped to one.","in":"header","name":"Summerbase-Project","required":false,"schema":{"format":"uuid","type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"additionalProperties":false,"description":"A page of results.","properties":{"data":{"items":{"$ref":"#/components/schemas/Integration"},"type":"array"},"has_more":{"description":"Whether another page follows this one.","example":false,"type":"boolean"},"next_cursor":{"anyOf":[{"type":"null"},{"description":"Pass back as `cursor` to fetch the next page. Null on the last page.","type":"string"}]},"object":{"enum":["list"],"type":"string"},"total_count":{"description":"How many records match altogether, counting every page. Only present when `include_total_count=true` was sent, on the first page.","minimum":0,"type":"integer"}},"required":["object","data","has_more"],"type":"object"}}},"description":"Returns a list of the project's integrations."},"default":{"$ref":"#/components/responses/Error"}},"summary":"List integrations","tags":["Integrations"]},"post":{"callbacks":{},"deprecated":false,"description":"Add an integration to the project you are working in.\n\nA source's slug is required, and fixed from here on: it is how records\nfrom it are identified — `warehouse:acct-9` — so your code hard-codes it.\n\nA `custom` integration is one your own code writes to: bind an API key\nto it and everything that key writes is attributed here, separately from\nanything else. A provider integration, `stripe`, is created empty and\nthen connected with the provider's own action, which is what starts the\nimport.\n","operationId":"create_integration","parameters":[{"description":"Relationships to include in the response instead of fetching separately. A dotted path expands what an expanded record can expand in turn — at most 4 deep, 10 paths a request, and never back to a record already on the path.","in":"query","name":"expand[]","required":false,"schema":{"items":{"enum":["current_import"],"type":"string"},"maxItems":10,"type":"array"}},{"description":"The organization to work within. Optional for an API key, which is already scoped to one; naming a different one is refused.","in":"header","name":"Summerbase-Organization","required":false,"schema":{"format":"uuid","type":"string"}},{"description":"The project to work within. Its organization is implied, so this alone is enough. Optional for an API key, which is already scoped to one.","in":"header","name":"Summerbase-Project","required":false,"schema":{"format":"uuid","type":"string"}}],"requestBody":{"content":{"application/json":{"example":{"name":"Backend","slug":"backend"},"schema":{"additionalProperties":false,"properties":{"name":{"description":"What the integration is called, for people reading a screen.","example":"Backend","maxLength":255,"minLength":1,"type":"string"},"priority":{"anyOf":[{"type":"null"},{"type":"integer"}],"description":"Where this source sits when resolving `name` and `email`. Lower wins, and\nthe Summerbase integration starts at the top so a manual correction\noutranks a provider sync. It orders nothing else — every other property\nis namespaced by source and never arbitrated. Defaults to 10 for a\n`custom` integration and 100 for a provider's. Null for an integration\nthat is not a source.\n"},"provider_code":{"description":"The code of the system the integration connects to. `custom` is one you\nwrite to yourself, and the only one that accepts writes; `stripe` is\nconnected with `connect_stripe` and fetches for itself.\n","enum":["custom","stripe","summerbase"],"type":"string"},"slug":{"anyOf":[{"type":"null"},{"maxLength":100,"minLength":1,"type":"string"}],"description":"How a source's records are identified — the `stripe` in `stripe:cus_123`.\nLowercase letters, digits, hyphens and underscores; unique within the\nproject and **fixed once the integration exists**, because your code\nhard-codes it. A reference shaped like an id is read as one, so a slug\nmay not look like a UUID. Null for an integration that is not a source.\n","example":"backend"}},"required":["name"],"type":"object"}}},"required":true},"responses":{"201":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Integration"}}},"description":"Returns the new integration object."},"default":{"$ref":"#/components/responses/Error"}},"summary":"Create an integration","tags":["Integrations"]}},"/v1/integrations/{id}":{"get":{"callbacks":{},"deprecated":false,"description":"Fetch one integration of the project you are working in.","operationId":"get_integration","parameters":[{"description":"The integration's slug — `stripe` — or its Summerbase id.","example":"0198f1a3-0b7c-7e25-9f41-6a2d8c3e5b19","in":"path","name":"id","required":true,"schema":{"type":"string"}},{"description":"Relationships to include in the response instead of fetching separately. A dotted path expands what an expanded record can expand in turn — at most 4 deep, 10 paths a request, and never back to a record already on the path.","in":"query","name":"expand[]","required":false,"schema":{"items":{"enum":["current_import"],"type":"string"},"maxItems":10,"type":"array"}},{"description":"The organization to work within. Optional for an API key, which is already scoped to one; naming a different one is refused.","in":"header","name":"Summerbase-Organization","required":false,"schema":{"format":"uuid","type":"string"}},{"description":"The project to work within. Its organization is implied, so this alone is enough. Optional for an API key, which is already scoped to one.","in":"header","name":"Summerbase-Project","required":false,"schema":{"format":"uuid","type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Integration"}}},"description":"Returns the integration object."},"default":{"$ref":"#/components/responses/Error"}},"summary":"Get an integration","tags":["Integrations"]},"patch":{"callbacks":{},"deprecated":false,"description":"Rename the integration, and, for a source, set where it sits in the\norder that resolves `name` and `email`. A source's slug does not change\n— records are identified by it.\n","operationId":"update_integration","parameters":[{"description":"The integration's slug — `stripe` — or its Summerbase id.","example":"0198f1a3-0b7c-7e25-9f41-6a2d8c3e5b19","in":"path","name":"id","required":true,"schema":{"type":"string"}},{"description":"Relationships to include in the response instead of fetching separately. A dotted path expands what an expanded record can expand in turn — at most 4 deep, 10 paths a request, and never back to a record already on the path.","in":"query","name":"expand[]","required":false,"schema":{"items":{"enum":["current_import"],"type":"string"},"maxItems":10,"type":"array"}},{"description":"The organization to work within. Optional for an API key, which is already scoped to one; naming a different one is refused.","in":"header","name":"Summerbase-Organization","required":false,"schema":{"format":"uuid","type":"string"}},{"description":"The project to work within. Its organization is implied, so this alone is enough. Optional for an API key, which is already scoped to one.","in":"header","name":"Summerbase-Project","required":false,"schema":{"format":"uuid","type":"string"}}],"requestBody":{"content":{"application/json":{"example":{"priority":1},"schema":{"additionalProperties":false,"properties":{"name":{"description":"What the integration is called, for people reading a screen.","maxLength":255,"minLength":1,"type":"string"},"priority":{"anyOf":[{"type":"null"},{"type":"integer"}],"description":"Where this source sits when resolving `name` and `email`. Lower wins, and\nthe Summerbase integration starts at the top so a manual correction\noutranks a provider sync. It orders nothing else — every other property\nis namespaced by source and never arbitrated. Defaults to 10 for a\n`custom` integration and 100 for a provider's. Null for an integration\nthat is not a source.\n","example":1}},"type":"object"}}},"required":false},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Integration"}}},"description":"Returns the updated integration object."},"default":{"$ref":"#/components/responses/Error"}},"summary":"Update an integration","tags":["Integrations"]}},"/v1/integrations/{id}/webhook_events":{"get":{"callbacks":{},"deprecated":false,"description":"The events a webhook endpoint at the integration's `webhook_url` has to\nselect, for one installed by hand. None for an integration whose provider\nposts no webhooks.\n","operationId":"get_integration_webhook_events","parameters":[{"description":"The integration's slug — `stripe` — or its Summerbase id.","example":"0198f1a3-0b7c-7e25-9f41-6a2d8c3e5b19","in":"path","name":"id","required":true,"schema":{"type":"string"}},{"description":"The organization to work within. Optional for an API key, which is already scoped to one; naming a different one is refused.","in":"header","name":"Summerbase-Organization","required":false,"schema":{"format":"uuid","type":"string"}},{"description":"The project to work within. Its organization is implied, so this alone is enough. Optional for an API key, which is already scoped to one.","in":"header","name":"Summerbase-Project","required":false,"schema":{"format":"uuid","type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/WebhookEvents"}}},"description":"Returns the names of the events a webhook endpoint for this integration has to select, in `events`. The list is empty for an integration whose provider sends no webhooks."},"default":{"$ref":"#/components/responses/Error"}},"summary":"Get an integration's webhook events","tags":["Integrations"]}},"/v1/integrations/{integration_id}/log":{"get":{"callbacks":{},"deprecated":false,"description":"One integration's log, newest first. Entries are kept for thirty days.","operationId":"list_integration_log","parameters":[{"description":"The integration whose log to read.","example":"0198f1a3-0b7c-7e25-9f41-6a2d8c3e5b19","in":"path","name":"integration_id","required":true,"schema":{"format":"uuid","type":"string"}},{"description":"How many records to return, 1 to 100. Defaults to 20.","in":"query","name":"limit","required":false,"schema":{"default":20,"maximum":100,"minimum":1,"type":"integer"}},{"description":"The `next_cursor` of the previous page, to read the page after it.","in":"query","name":"cursor","required":false,"schema":{"type":"string"}},{"description":"Whether to add `total_count`, how many records match altogether. On the first page only: it is the same number on every page of one list, so keep the first page's, and it is refused beside a `cursor`.","in":"query","name":"include_total_count","required":false,"schema":{"default":false,"type":"boolean"}},{"description":"Relationships to include in the response instead of fetching separately. A dotted path expands what an expanded record can expand in turn — at most 4 deep, 10 paths a request, and never back to a record already on the path.","in":"query","name":"expand[]","required":false,"schema":{"items":{"enum":["integration","integration.current_import"],"type":"string"},"maxItems":10,"type":"array"}},{"description":"The organization to work within. Optional for an API key, which is already scoped to one; naming a different one is refused.","in":"header","name":"Summerbase-Organization","required":false,"schema":{"format":"uuid","type":"string"}},{"description":"The project to work within. Its organization is implied, so this alone is enough. Optional for an API key, which is already scoped to one.","in":"header","name":"Summerbase-Project","required":false,"schema":{"format":"uuid","type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"additionalProperties":false,"description":"A page of results.","properties":{"data":{"items":{"$ref":"#/components/schemas/IntegrationLogEntry"},"type":"array"},"has_more":{"description":"Whether another page follows this one.","example":false,"type":"boolean"},"next_cursor":{"anyOf":[{"type":"null"},{"description":"Pass back as `cursor` to fetch the next page. Null on the last page.","type":"string"}]},"object":{"enum":["list"],"type":"string"},"total_count":{"description":"How many records match altogether, counting every page. Only present when `include_total_count=true` was sent, on the first page.","minimum":0,"type":"integer"}},"required":["object","data","has_more"],"type":"object"}}},"description":"Returns a list of the integration's log entries, newest first."},"default":{"$ref":"#/components/responses/Error"}},"summary":"List an integration's log entries","tags":["Integrations"]}},"/v1/customers/search":{"post":{"callbacks":{},"deprecated":false,"description":"Search the customers of the project you are working in.\n\n`search` matches names and email addresses, and a customer whose ID in\nany source is exactly the text. `filter` is a condition over\nwhat is known about each customer: their own fields, what each source\nsays about them, and their subscriptions and items. `sort` is the\ncustomers most recently first seen by a\nsource first unless asked otherwise.\n\nThe list briefly lags a write: a customer just created or just changed\nappears here, under their new values, shortly after. The records themselves\nare current.\n","operationId":"search_customers","parameters":[{"description":"The organization to work within. Optional for an API key, which is already scoped to one; naming a different one is refused.","in":"header","name":"Summerbase-Organization","required":false,"schema":{"format":"uuid","type":"string"}},{"description":"The project to work within. Its organization is implied, so this alone is enough. Optional for an API key, which is already scoped to one.","in":"header","name":"Summerbase-Project","required":false,"schema":{"format":"uuid","type":"string"}}],"requestBody":{"content":{"application/json":{"example":{"filter":{"equals":"paying","key":"customer.status"},"limit":10,"sort":"-mrr"},"schema":{"additionalProperties":false,"properties":{"cursor":{"description":"The `next_cursor` of the previous page, to read the page after it.","type":"string"},"expand":{"description":"Relationships to include in the response instead of fetching separately. A dotted path expands what an expanded record can expand in turn — at most 4 deep, 10 paths a request, and never back to a record already on the path.","items":{"enum":["country","erasures","subscriptions","erasures.requested_by","subscriptions.currency","subscriptions.items","subscriptions.owner_source","subscriptions.items.price","subscriptions.owner_source.current_import","subscriptions.items.price.owner_source","subscriptions.items.price.plan"],"type":"string"},"maxItems":10,"type":"array"},"filter":{"allOf":[{"$ref":"#/components/schemas/FilterCondition"}],"description":"Only customers the condition matches: any key about the customer,\nits subscriptions or their items, as [`GET /v1/fields`](/docs/api/operations/list-fields) lists them. A\nkey that names nothing is refused, with what is filterable. A comparison reads the value now\nunless it names an instant; a list has no period, so `window`\nis refused. See [Filters](/docs/api/filters) for how to write one.\n","example":{"equals":"paying","key":"customer.status"}},"include_total_count":{"default":false,"description":"Whether to add `total_count`, how many records match altogether. On the first page only: it is the same number on every page of one list, so keep the first page's, and it is refused beside a `cursor`.","type":"boolean"},"limit":{"default":20,"description":"How many records to return, 1 to 100. Defaults to 20.","example":10,"maximum":100,"minimum":1,"type":"integer"},"search":{"description":"Only customers whose name or email address contains this, or whose ID\nin any source is exactly this — `cus_123`.\n","maxLength":255,"type":"string"},"sort":{"description":"The order — `-mrr` or `-arr` for the most valuable first, `name`,\n`-first_paying_at` for the newest cohort first, or\n`properties.backend.seats` for what a source said.\n\nThe order: a key, `-` before it for descending, commas between up to 3 keys — `-created_at`, `name`, `-mrr,name`. The keys are `created_at`, `name`, `email`, `status`, `signed_up_at`, `country_code`, `first_seen_at`, `first_subscribed_at`, `first_paying_at`, `churned_at`, `mrr`, `arr`, and `properties.<source>.<key>` for what a source said. Text sorts ignoring case; a record with no value for the key comes last, whichever way the list runs.","example":"-mrr","type":"string"}},"type":"object"}}},"required":false},"responses":{"200":{"content":{"application/json":{"schema":{"additionalProperties":false,"description":"A page of results.","properties":{"data":{"items":{"$ref":"#/components/schemas/Customer"},"type":"array"},"has_more":{"description":"Whether another page follows this one.","example":false,"type":"boolean"},"next_cursor":{"anyOf":[{"type":"null"},{"description":"Pass back as `cursor` to fetch the next page. Null on the last page.","type":"string"}]},"object":{"enum":["list"],"type":"string"},"total_count":{"description":"How many records match altogether, counting every page. Only present when `include_total_count=true` was sent, on the first page.","minimum":0,"type":"integer"}},"required":["object","data","has_more"],"type":"object"}}},"description":"Returns a list of the customers that match."},"default":{"$ref":"#/components/responses/Error"}},"summary":"Search customers","tags":["Customers"],"x-analytics-request":true}},"/v1/customers/{id}":{"get":{"callbacks":{},"deprecated":false,"description":"Fetch one customer, by Summerbase id or by any source's id for them,\nsuch as `stripe:cus_123`.\n","operationId":"get_customer","parameters":[{"description":"The customer's Summerbase id, or an identity ref: a source slug and that\nsource's own id for them, joined by a colon — `stripe:cus_123`.\n","example":"0199a7c2-4e1b-7f38-b9d2-5a6c8e0f1d73","in":"path","name":"id","required":true,"schema":{"type":"string"}},{"description":"Relationships to include in the response instead of fetching separately. A dotted path expands what an expanded record can expand in turn — at most 4 deep, 10 paths a request, and never back to a record already on the path.","in":"query","name":"expand[]","required":false,"schema":{"items":{"enum":["country","erasures","subscriptions","erasures.requested_by","subscriptions.currency","subscriptions.items","subscriptions.owner_source","subscriptions.items.price","subscriptions.owner_source.current_import","subscriptions.items.price.owner_source","subscriptions.items.price.plan"],"type":"string"},"maxItems":10,"type":"array"}},{"description":"The organization to work within. Optional for an API key, which is already scoped to one; naming a different one is refused.","in":"header","name":"Summerbase-Organization","required":false,"schema":{"format":"uuid","type":"string"}},{"description":"The project to work within. Its organization is implied, so this alone is enough. Optional for an API key, which is already scoped to one.","in":"header","name":"Summerbase-Project","required":false,"schema":{"format":"uuid","type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Customer"}}},"description":"Returns the customer object."},"default":{"$ref":"#/components/responses/Error"}},"summary":"Get a customer","tags":["Customers"]},"patch":{"callbacks":{},"deprecated":false,"description":"Record what you know about a customer. Only the keys you mention change;\n`null` clears one.\n\nProperties are attributed to your own source, so they never overwrite what\nanother source said. `identities` teaches us what other systems call this\ncustomer, which is how a backend sync links its own id to a provider's.\n\nA value you change or clear is kept in the customer's history, which is\nwhat past figures are read from. To remove one for good, reimport the\nhistory without it or erase it.\n","operationId":"update_customer","parameters":[{"description":"The customer's Summerbase id, or an identity ref: a source slug and that\nsource's own id for them, joined by a colon — `stripe:cus_123`.\n","example":"backend:acct_4821","in":"path","name":"id","required":true,"schema":{"type":"string"}},{"description":"Relationships to include in the response instead of fetching separately. A dotted path expands what an expanded record can expand in turn — at most 4 deep, 10 paths a request, and never back to a record already on the path.","in":"query","name":"expand[]","required":false,"schema":{"items":{"enum":["country","erasures","subscriptions","erasures.requested_by","subscriptions.currency","subscriptions.items","subscriptions.owner_source","subscriptions.items.price","subscriptions.owner_source.current_import","subscriptions.items.price.owner_source","subscriptions.items.price.plan"],"type":"string"},"maxItems":10,"type":"array"}},{"description":"The organization to work within. Optional for an API key, which is already scoped to one; naming a different one is refused.","in":"header","name":"Summerbase-Organization","required":false,"schema":{"format":"uuid","type":"string"}},{"description":"The project to work within. Its organization is implied, so this alone is enough. Optional for an API key, which is already scoped to one.","in":"header","name":"Summerbase-Project","required":false,"schema":{"format":"uuid","type":"string"}}],"requestBody":{"content":{"application/json":{"example":{"properties":{"email":"billing@northwind.example.com","name":"Northwind Labs","seats":25}},"schema":{"additionalProperties":false,"properties":{"identities":{"description":"What other sources call this customer, as `source:id` — `[\"stripe:cus_123\"]`. One that already identifies another customer is refused. An identity is kept for as long as the customer is and is never erased, so never use personal data, such as an email address, as one.\n\nWhat each source calls this record, as `<source>:<id in that source>`. Any entry addresses it — `stripe:cus_123`.","items":{"type":"string"},"type":"array"},"on_warning":{"description":"What to do about the parts of this write that cannot be applied as\nsent.\n\n`warn` applies everything it can and reports the rest in `warnings`, so\none unusable property never costs you a subscription's amounts. `error`\napplies none of it unless all of it applies: the write is rolled back\nand every warning comes back as an error instead.\n\nSend `error` from a form, where somebody is waiting to be told what to\nfix. Leave it alone in an ingest client, which wants the revenue to\nland either way.\n","enum":["warn","error"],"type":"string"},"properties":{"additionalProperties":{"anyOf":[{"type":"string"},{"type":"number"},{"type":"boolean"},{"$ref":"#/components/schemas/PropertyInput"}]},"description":"Properties to record in your source.\n\nProperty keys and the values to set. Only the keys named here change; `null` clears one.","example":{"email":"billing@northwind.example.com","name":"Northwind Labs","seats":25},"type":"object"}},"type":"object"}}},"required":false},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UpdateCustomerResult"}}},"description":"Returns the customer object, created if the id was new, with any warnings in `response_metadata`."},"default":{"$ref":"#/components/responses/Error"}},"summary":"Update a customer","tags":["Customers"]}},"/v1/customers":{"post":{"callbacks":{},"deprecated":false,"description":"Creates a customer, with a Summerbase id assigned to it — what an\nintegration sends when no system has an id for this company yet.\n\nNaming one of your own ids instead creates the customer under that name,\nso a later write about the same id reaches the same record.\n","operationId":"create_customer","parameters":[{"description":"Relationships to include in the response instead of fetching separately. A dotted path expands what an expanded record can expand in turn — at most 4 deep, 10 paths a request, and never back to a record already on the path.","in":"query","name":"expand[]","required":false,"schema":{"items":{"enum":["country","erasures","subscriptions","erasures.requested_by","subscriptions.currency","subscriptions.items","subscriptions.owner_source","subscriptions.items.price","subscriptions.owner_source.current_import","subscriptions.items.price.owner_source","subscriptions.items.price.plan"],"type":"string"},"maxItems":10,"type":"array"}},{"description":"The organization to work within. Optional for an API key, which is already scoped to one; naming a different one is refused.","in":"header","name":"Summerbase-Organization","required":false,"schema":{"format":"uuid","type":"string"}},{"description":"The project to work within. Its organization is implied, so this alone is enough. Optional for an API key, which is already scoped to one.","in":"header","name":"Summerbase-Project","required":false,"schema":{"format":"uuid","type":"string"}}],"requestBody":{"content":{"application/json":{"example":{"properties":{"email":"billing@northwind.example.com","name":"Northwind Labs"}},"schema":{"additionalProperties":false,"properties":{"on_warning":{"description":"What to do about the parts of this write that cannot be applied as\nsent.\n\n`warn` applies everything it can and reports the rest in `warnings`, so\none unusable property never costs you a subscription's amounts. `error`\napplies none of it unless all of it applies: the write is rolled back\nand every warning comes back as an error instead.\n\nSend `error` from a form, where somebody is waiting to be told what to\nfix. Leave it alone in an ingest client, which wants the revenue to\nland either way.\n","enum":["warn","error"],"type":"string"},"properties":{"additionalProperties":{"anyOf":[{"type":"string"},{"type":"number"},{"type":"boolean"},{"$ref":"#/components/schemas/PropertyInput"}]},"description":"Properties to record in your source.\n\nProperty keys and the values to set. Only the keys named here change; `null` clears one.","example":{"email":"billing@northwind.example.com","name":"Northwind Labs"},"type":"object"}},"type":"object"}}},"required":false},"responses":{"201":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateCustomerResult"}}},"description":"Returns the new customer object, with any warnings in `response_metadata`."},"default":{"$ref":"#/components/responses/Error"}},"summary":"Create a customer","tags":["Customers"]}},"/v1/customers/{id}/merge":{"post":{"callbacks":{},"deprecated":false,"description":"Merge other customers into this one: their identities, properties and\nsubscriptions move here, and they are deleted. Every figure then counts\none customer where there were several, as far back as their history\ngoes.\n\nIt cannot be undone. Any id a merged customer had addresses this one\nafterwards.\n","operationId":"merge_customers","parameters":[{"description":"The customer's Summerbase id, or an identity ref: a source slug and that\nsource's own id for them, joined by a colon — `stripe:cus_123`.\n","example":"backend:acct_4821","in":"path","name":"id","required":true,"schema":{"type":"string"}},{"description":"Relationships to include in the response instead of fetching separately. A dotted path expands what an expanded record can expand in turn — at most 4 deep, 10 paths a request, and never back to a record already on the path.","in":"query","name":"expand[]","required":false,"schema":{"items":{"enum":["country","erasures","subscriptions","erasures.requested_by","subscriptions.currency","subscriptions.items","subscriptions.owner_source","subscriptions.items.price","subscriptions.owner_source.current_import","subscriptions.items.price.owner_source","subscriptions.items.price.plan"],"type":"string"},"maxItems":10,"type":"array"}},{"description":"The organization to work within. Optional for an API key, which is already scoped to one; naming a different one is refused.","in":"header","name":"Summerbase-Organization","required":false,"schema":{"format":"uuid","type":"string"}},{"description":"The project to work within. Its organization is implied, so this alone is enough. Optional for an API key, which is already scoped to one.","in":"header","name":"Summerbase-Project","required":false,"schema":{"format":"uuid","type":"string"}}],"requestBody":{"content":{"application/json":{"example":{"merged_customer_ids":["stripe:cus_QX7bN2kLp4Rt9v"]},"schema":{"additionalProperties":false,"properties":{"merged_customer_ids":{"description":"The customers to merge into this one, which are deleted — each a\nSummerbase id, or a source identity.\n","example":["stripe:cus_QX7bN2kLp4Rt9v"],"items":{"maxLength":320,"minLength":1,"type":"string"},"type":"array"}},"required":["merged_customer_ids"],"type":"object"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Customer"}}},"description":"Returns the customer the others were merged into."},"default":{"$ref":"#/components/responses/Error"}},"summary":"Merge customers into another","tags":["Customers"]}},"/v1/customers/{id}/reimport":{"post":{"callbacks":{},"deprecated":false,"description":"Replace everything your source has said about a customer with its\nhistory, sent all at once and oldest first — the one way to write the\npast.\n\nEach entry is a patch onto the one before it, applied through the same\nrules as an update; the first is applied to a customer holding nothing\nof yours. Your properties on the customer are replaced; other sources'\nproperties and every identity stay, and `identities` adds to them. Lands\nwhole or not at all.\n\nWhat it replaces is deleted for good: gone from every answer within\nmoments, and from every copy Summerbase keeps within a day.\n\nA customer that does not exist yet is created by its first entry.\n","operationId":"reimport_customer","parameters":[{"description":"The customer — a Summerbase id, or a source identity like `stripe:cus_123`.","example":"backend:acct_4821","in":"path","name":"id","required":true,"schema":{"maxLength":320,"minLength":1,"type":"string"}},{"description":"Relationships to include in the response instead of fetching separately. A dotted path expands what an expanded record can expand in turn — at most 4 deep, 10 paths a request, and never back to a record already on the path.","in":"query","name":"expand[]","required":false,"schema":{"items":{"enum":["country","erasures","subscriptions","erasures.requested_by","subscriptions.currency","subscriptions.items","subscriptions.owner_source","subscriptions.items.price","subscriptions.owner_source.current_import","subscriptions.items.price.owner_source","subscriptions.items.price.plan"],"type":"string"},"maxItems":10,"type":"array"}},{"description":"The organization to work within. Optional for an API key, which is already scoped to one; naming a different one is refused.","in":"header","name":"Summerbase-Organization","required":false,"schema":{"format":"uuid","type":"string"}},{"description":"The project to work within. Its organization is implied, so this alone is enough. Optional for an API key, which is already scoped to one.","in":"header","name":"Summerbase-Project","required":false,"schema":{"format":"uuid","type":"string"}}],"requestBody":{"content":{"application/json":{"example":{"history":[{"effective_at":"2025-11-03T16:20:00Z","properties":{"name":"Northwind Labs","seats":20}},{"effective_at":"2026-06-12T15:47:00Z","properties":{"seats":25}}]},"schema":{"additionalProperties":false,"properties":{"history":{"description":"The customer's history, oldest first. At most 10,000 entries.","example":[{"effective_at":"2025-11-03T16:20:00Z","properties":{"name":"Northwind Labs","seats":20}},{"effective_at":"2026-06-12T15:47:00Z","properties":{"seats":25}}],"items":{"additionalProperties":false,"properties":{"effective_at":{"description":"When this became true. Later than the entry before it.","format":"date-time","type":"string"},"properties":{"anyOf":[{"type":"null"},{"additionalProperties":{"anyOf":[{"type":"string"},{"type":"number"},{"type":"boolean"},{"$ref":"#/components/schemas/PropertyInput"}]},"type":"object"}],"description":"Properties in your source that changed at this moment; `null` clears one."}},"required":["effective_at"],"type":"object"},"type":"array"},"identities":{"description":"What other sources call this customer, as `source:id`. Attached once, beside\nthe history — an identity is an address rather than a value and has no\nmoment. One that already identifies another record is refused.\n\nWhat each source calls this record, as `<source>:<id in that source>`. Any entry addresses it — `stripe:cus_123`.","items":{"type":"string"},"type":"array"}},"required":["history"],"type":"object"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Customer"}}},"description":"Returns the customer object as its history leaves it."},"default":{"$ref":"#/components/responses/Error"}},"summary":"Reimport a customer's history","tags":["Customers"]}},"/v1/customers/find_duplicates":{"post":{"callbacks":{},"deprecated":false,"description":"Groups of customers that match by `fields` and so may be one customer\nknown more than once — to look over, and merge with `POST\n/v1/customers/{id}/merge`. A group of customers linked through a chain\nof matches is one group.\n","operationId":"find_duplicate_customers","parameters":[{"description":"The organization to work within. Optional for an API key, which is already scoped to one; naming a different one is refused.","in":"header","name":"Summerbase-Organization","required":false,"schema":{"format":"uuid","type":"string"}},{"description":"The project to work within. Its organization is implied, so this alone is enough. Optional for an API key, which is already scoped to one.","in":"header","name":"Summerbase-Project","required":false,"schema":{"format":"uuid","type":"string"}}],"requestBody":{"content":{"application/json":{"example":{"fields":["customer.external_ids.stripe-old","customer.external_ids.stripe-new"],"limit":100},"schema":{"additionalProperties":false,"properties":{"cursor":{"description":"The `next_cursor` of the previous page, to read the page after it.","type":"string"},"fields":{"description":"The two fields to compare, as `GET /v1/fields` lists them: a\ncustomer's IDs from one source, `customer.external_ids.stripe`, or a\ncustomer field holding text, `customer.email`. A customer matches\nanother when one of its values under the first is one of the other's\nunder the second, and the two may be the same field. With an ID on\neither side the values are compared exactly; otherwise trimmed and\nignoring case.\n","example":["customer.external_ids.stripe-old","customer.external_ids.stripe-new"],"items":{"type":"string"},"type":"array"},"include_total_count":{"default":false,"description":"Whether to add `total_count`, how many records match altogether. On the first page only: it is the same number on every page of one list, so keep the first page's, and it is refused beside a `cursor`.","type":"boolean"},"limit":{"default":20,"description":"How many records to return, 1 to 100. Defaults to 20.","example":100,"maximum":100,"minimum":1,"type":"integer"}},"required":["fields"],"type":"object"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"additionalProperties":false,"description":"A page of results.","properties":{"data":{"items":{"$ref":"#/components/schemas/DuplicateCustomers"},"type":"array"},"has_more":{"description":"Whether another page follows this one.","example":false,"type":"boolean"},"next_cursor":{"anyOf":[{"type":"null"},{"description":"Pass back as `cursor` to fetch the next page. Null on the last page.","type":"string"}]},"object":{"enum":["list"],"type":"string"},"total_count":{"description":"How many records match altogether, counting every page. Only present when `include_total_count=true` was sent, on the first page.","minimum":0,"type":"integer"}},"required":["object","data","has_more"],"type":"object"}}},"description":"Returns a list of the groups of customers that match."},"default":{"$ref":"#/components/responses/Error"}},"summary":"Find duplicate customers","tags":["Customers"]}},"/v1/customers/{id}/erase":{"post":{"callbacks":{},"deprecated":false,"description":"Erase a customer's personal data: the properties you name, in every\nsource's history, on the customer and on their subscriptions and\nitems. The customer, their subscriptions and their revenue stay.\n\nName properties as `GET /v1/fields` does — `customer.properties.backend.phone`\n— with `*` for any record type, any source or any key:\n`customer.properties.*.*` is everything every source says about the\ncustomer. `keep_properties` takes back what `erase_properties` matched.\nA `*` in place of the key never matches `signed_up_at` or\n`country_code`, which figures are computed from; name one to erase it,\nand the customer's figures change with it.\n\nIdentities are never erased, so a source should not use personal data,\nsuch as an email address, as one.\n\nErased values are deleted for good: gone from every answer within\nmoments, and from every copy Summerbase keeps within a day, when\n`completed_at` is set. A source\nthat sends a value again is writing a new one, which is kept. With\n`dry_run`, nothing is erased or stored: the answer is the erasure the\nrequest would make, its `id` names nothing, and\n`response_metadata.dry_run` is `true`. A request that matches\nnothing is stored all the same, as an erasure of nothing that is\nalready complete: the record that the request was carried out.\n","operationId":"erase_customer","parameters":[{"description":"The customer — a Summerbase id, or a source identity like `stripe:cus_123`.","example":"backend:acct_4821","in":"path","name":"id","required":true,"schema":{"maxLength":320,"minLength":1,"type":"string"}},{"description":"Relationships to include in the response instead of fetching separately. A dotted path expands what an expanded record can expand in turn — at most 4 deep, 10 paths a request, and never back to a record already on the path.","in":"query","name":"expand[]","required":false,"schema":{"items":{"enum":["customer","requested_by","customer.country","customer.subscriptions","customer.subscriptions.currency","customer.subscriptions.items","customer.subscriptions.owner_source","customer.subscriptions.items.price","customer.subscriptions.owner_source.current_import"],"type":"string"},"maxItems":10,"type":"array"}},{"description":"The organization to work within. Optional for an API key, which is already scoped to one; naming a different one is refused.","in":"header","name":"Summerbase-Organization","required":false,"schema":{"format":"uuid","type":"string"}},{"description":"The project to work within. Its organization is implied, so this alone is enough. Optional for an API key, which is already scoped to one.","in":"header","name":"Summerbase-Project","required":false,"schema":{"format":"uuid","type":"string"}}],"requestBody":{"content":{"application/json":{"example":{"dry_run":true,"erase_properties":["customer.properties.*.*","subscription.properties.crm.*"],"keep_properties":["customer.properties.backend.industry"]},"schema":{"additionalProperties":false,"properties":{"dry_run":{"description":"Answer with what would be erased, and erase nothing.","example":true,"type":"boolean"},"erase_properties":{"description":"The properties to erase, as `record.properties.source.key`, where the\nrecord is `customer`, `subscription` or `subscription_item` and any of\nthe three parts may be `*`. A subscription's properties are erased on\nevery subscription the customer has.\n","example":["customer.properties.*.*","subscription.properties.crm.*"],"items":{"type":"string"},"type":"array"},"keep_properties":{"description":"Properties to leave alone although `erase_properties` matched them, in\nthe same form.\n","example":["customer.properties.backend.industry"],"items":{"type":"string"},"type":"array"}},"type":"object"}}},"required":false},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CustomerErasure"}}},"description":"Returns the customer erasure object."},"default":{"$ref":"#/components/responses/Error"}},"summary":"Erase a customer's personal data","tags":["Customers"]}},"/v1/subscriptions":{"get":{"callbacks":{},"deprecated":false,"description":"The subscriptions of the project you are working in.","operationId":"list_subscriptions","parameters":[{"description":"Only this customer's. Our id, or any source identity for them.","in":"query","name":"customer_id","required":false,"schema":{"type":"string"}},{"description":"Only subscriptions in this state.","in":"query","name":"status","required":false,"schema":{"enum":["pending","trialing","active","paused","canceled"],"type":"string"}},{"description":"How many records to return, 1 to 100. Defaults to 20.","in":"query","name":"limit","required":false,"schema":{"default":20,"maximum":100,"minimum":1,"type":"integer"}},{"description":"The `next_cursor` of the previous page, to read the page after it.","in":"query","name":"cursor","required":false,"schema":{"type":"string"}},{"description":"Whether to add `total_count`, how many records match altogether. On the first page only: it is the same number on every page of one list, so keep the first page's, and it is refused beside a `cursor`.","in":"query","name":"include_total_count","required":false,"schema":{"default":false,"type":"boolean"}},{"description":"Relationships to include in the response instead of fetching separately. A dotted path expands what an expanded record can expand in turn — at most 4 deep, 10 paths a request, and never back to a record already on the path.","in":"query","name":"expand[]","required":false,"schema":{"items":{"enum":["currency","customer","items","owner_source","customer.country","customer.erasures","items.price","owner_source.current_import","customer.erasures.requested_by","items.price.owner_source","items.price.plan","items.price.owner_source.current_import"],"type":"string"},"maxItems":10,"type":"array"}},{"description":"The organization to work within. Optional for an API key, which is already scoped to one; naming a different one is refused.","in":"header","name":"Summerbase-Organization","required":false,"schema":{"format":"uuid","type":"string"}},{"description":"The project to work within. Its organization is implied, so this alone is enough. Optional for an API key, which is already scoped to one.","in":"header","name":"Summerbase-Project","required":false,"schema":{"format":"uuid","type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"additionalProperties":false,"description":"A page of results.","properties":{"data":{"items":{"$ref":"#/components/schemas/Subscription"},"type":"array"},"has_more":{"description":"Whether another page follows this one.","example":false,"type":"boolean"},"next_cursor":{"anyOf":[{"type":"null"},{"description":"Pass back as `cursor` to fetch the next page. Null on the last page.","type":"string"}]},"object":{"enum":["list"],"type":"string"},"total_count":{"description":"How many records match altogether, counting every page. Only present when `include_total_count=true` was sent, on the first page.","minimum":0,"type":"integer"}},"required":["object","data","has_more"],"type":"object"}}},"description":"Returns a list of subscriptions."},"default":{"$ref":"#/components/responses/Error"}},"summary":"List subscriptions","tags":["Subscriptions"]},"post":{"callbacks":{},"deprecated":false,"description":"Creates a subscription. Your source owns it, and is the only one that may\nmove its status, its dates or its items afterwards — which is why it takes\n`claim_ownership: true`.\n\nA Summerbase id is assigned to it, which is what a subscription billed\noutside any system that would give it one needs. Naming one of your own\nids instead creates it under that name.\n","operationId":"create_subscription","parameters":[{"description":"Relationships to include in the response instead of fetching separately. A dotted path expands what an expanded record can expand in turn — at most 4 deep, 10 paths a request, and never back to a record already on the path.","in":"query","name":"expand[]","required":false,"schema":{"items":{"enum":["currency","customer","items","owner_source","customer.country","customer.erasures","items.price","owner_source.current_import","customer.erasures.requested_by","items.price.owner_source","items.price.plan","items.price.owner_source.current_import"],"type":"string"},"maxItems":10,"type":"array"}},{"description":"The organization to work within. Optional for an API key, which is already scoped to one; naming a different one is refused.","in":"header","name":"Summerbase-Organization","required":false,"schema":{"format":"uuid","type":"string"}},{"description":"The project to work within. Its organization is implied, so this alone is enough. Optional for an API key, which is already scoped to one.","in":"header","name":"Summerbase-Project","required":false,"schema":{"format":"uuid","type":"string"}}],"requestBody":{"content":{"application/json":{"example":{"claim_ownership":true,"currency_code":"USD","current_period_ends_at":"2026-11-01T10:04:00Z","customer_id":"backend:acct_4821","items":[{"id":"backend:item_9001","interval":"month","price_id":"backend:growth-monthly","quantity":"25","unit_amount":"50.00"}],"status":"active"},"schema":{"additionalProperties":false,"properties":{"cancels_at":{"anyOf":[{"type":"null"},{"format":"date-time","type":"string"}],"description":"A cancellation already booked. It is still in service until then, and\nthe source says so with `status: canceled` when it stops.\n"},"claim_ownership":{"description":"Say `true` to create this subscription as the source it lives in — the\nonly one that may write its status, its dates and its items from then\non. A subscription is never created without it, so a write meant to\nannotate one that does not exist yet is refused rather than creating it\nunder the wrong owner. On an existing subscription it changes nothing\nfor the owner and is refused for anyone else.\n","example":true,"type":"boolean"},"currency_code":{"description":"The currency it bills in, by its ISO 4217 code.","example":"USD","maxLength":3,"minLength":3,"type":"string"},"current_period_ends_at":{"anyOf":[{"type":"null"},{"format":"date-time","type":"string"}],"description":"When the current billing period ends. The next charge is due then,\nunless a cancellation is booked for it.\n","example":"2026-11-01T10:04:00Z"},"customer_id":{"description":"Who is subscribed. A Summerbase id, or any source identity for them like\n`stripe:cus_123`, which creates the customer if it is new.\n","example":"backend:acct_4821","type":"string"},"ended_at":{"anyOf":[{"type":"null"},{"format":"date-time","type":"string"}],"description":"When service stopped, beside `status: canceled`. Clear it, with `status`\nback to `active`, to resume a subscription; the same id may be brought\nback as often as the source does so.\n"},"items":{"description":"The items it is made of.","example":[{"id":"backend:item_9001","interval":"month","price_id":"backend:growth-monthly","quantity":"25","unit_amount":"50.00"}],"items":{"$ref":"#/components/schemas/SubscriptionItemInput"},"type":"array"},"on_warning":{"description":"What to do about the parts of this write that cannot be applied as\nsent.\n\n`warn` applies everything it can and reports the rest in `warnings`, so\none unusable property never costs you a subscription's amounts. `error`\napplies none of it unless all of it applies: the write is rolled back\nand every warning comes back as an error instead.\n\nSend `error` from a form, where somebody is waiting to be told what to\nfix. Leave it alone in an ingest client, which wants the revenue to\nland either way.\n","enum":["warn","error"],"type":"string"},"properties":{"additionalProperties":{"anyOf":[{"type":"string"},{"type":"number"},{"type":"boolean"},{"$ref":"#/components/schemas/PropertyInput"}]},"description":"Properties to record in your source.\n\nProperty keys and the values to set. Only the keys named here change; `null` clears one.","type":"object"},"status":{"description":"Where it is in its life. Not whether it has been paid for.","enum":["pending","trialing","active","paused","canceled"],"example":"active","type":"string"},"trial_ends_at":{"anyOf":[{"type":"null"},{"format":"date-time","type":"string"}],"description":"When the trial ends, or ended. A subscription that trials starts\ntrialing, so when it began is `started_at` and its status history.\n"}},"required":["currency_code","customer_id","status"],"type":"object"}}},"required":true},"responses":{"201":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateSubscriptionResult"}}},"description":"Returns the new subscription object, with any warnings in `response_metadata`."},"default":{"$ref":"#/components/responses/Error"}},"summary":"Create a subscription","tags":["Subscriptions"]}},"/v1/subscriptions/{id}":{"get":{"callbacks":{},"deprecated":false,"description":"Fetch one subscription, by Summerbase id or by any source's id for it.","operationId":"get_subscription","parameters":[{"description":"Its Summerbase id, or an identity ref: a source slug and that source's\nown id for it, joined by a colon — `stripe:sub_123`.\n","example":"0199a7c2-5b3e-7a14-8c6d-2f9e4b7a1c05","in":"path","name":"id","required":true,"schema":{"type":"string"}},{"description":"Relationships to include in the response instead of fetching separately. A dotted path expands what an expanded record can expand in turn — at most 4 deep, 10 paths a request, and never back to a record already on the path.","in":"query","name":"expand[]","required":false,"schema":{"items":{"enum":["currency","customer","items","owner_source","customer.country","customer.erasures","items.price","owner_source.current_import","customer.erasures.requested_by","items.price.owner_source","items.price.plan","items.price.owner_source.current_import"],"type":"string"},"maxItems":10,"type":"array"}},{"description":"The organization to work within. Optional for an API key, which is already scoped to one; naming a different one is refused.","in":"header","name":"Summerbase-Organization","required":false,"schema":{"format":"uuid","type":"string"}},{"description":"The project to work within. Its organization is implied, so this alone is enough. Optional for an API key, which is already scoped to one.","in":"header","name":"Summerbase-Project","required":false,"schema":{"format":"uuid","type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Subscription"}}},"description":"Returns the subscription object."},"default":{"$ref":"#/components/responses/Error"}},"summary":"Get a subscription","tags":["Subscriptions"]},"patch":{"callbacks":{},"deprecated":false,"description":"Report what you know about a subscription. Only the fields you mention\nchange; `null` clears one, and `items` replaces the whole set.\n\n**Its own fields are the owning source's.** Anyone may attach `properties`\nand `identities`; only the source that owns it may move its status, dates\nor items.\n\nA value you change or clear is kept in the subscription's history, which\nis what past figures are read from. To remove one for good, reimport the\nhistory without it, or erase a property.\n","operationId":"update_subscription","parameters":[{"description":"Its Summerbase id, or an identity ref: a source slug and that source's\nown id for it, joined by a colon — `stripe:sub_123`.\n","example":"backend:sub_7731","in":"path","name":"id","required":true,"schema":{"type":"string"}},{"description":"Relationships to include in the response instead of fetching separately. A dotted path expands what an expanded record can expand in turn — at most 4 deep, 10 paths a request, and never back to a record already on the path.","in":"query","name":"expand[]","required":false,"schema":{"items":{"enum":["currency","customer","items","owner_source","customer.country","customer.erasures","items.price","owner_source.current_import","customer.erasures.requested_by","items.price.owner_source","items.price.plan","items.price.owner_source.current_import"],"type":"string"},"maxItems":10,"type":"array"}},{"description":"The organization to work within. Optional for an API key, which is already scoped to one; naming a different one is refused.","in":"header","name":"Summerbase-Organization","required":false,"schema":{"format":"uuid","type":"string"}},{"description":"The project to work within. Its organization is implied, so this alone is enough. Optional for an API key, which is already scoped to one.","in":"header","name":"Summerbase-Project","required":false,"schema":{"format":"uuid","type":"string"}}],"requestBody":{"content":{"application/json":{"example":{"claim_ownership":true,"currency_code":"USD","current_period_ends_at":"2026-11-01T10:04:00Z","customer_id":"backend:acct_4821","items":[{"id":"backend:item_9001","interval":"month","price_id":"backend:growth-monthly","quantity":"25","unit_amount":"50.00"}],"status":"active"},"schema":{"additionalProperties":false,"properties":{"cancels_at":{"anyOf":[{"type":"null"},{"format":"date-time","type":"string"}],"description":"A cancellation already booked. It is still in service until then, and\nthe source says so with `status: canceled` when it stops.\n"},"claim_ownership":{"description":"Say `true` to create this subscription as the source it lives in — the\nonly one that may write its status, its dates and its items from then\non. A subscription is never created without it, so a write meant to\nannotate one that does not exist yet is refused rather than creating it\nunder the wrong owner. On an existing subscription it changes nothing\nfor the owner and is refused for anyone else.\n","example":true,"type":"boolean"},"currency_code":{"description":"The currency it bills in, by its ISO 4217 code.","example":"USD","maxLength":3,"minLength":3,"type":"string"},"current_period_ends_at":{"anyOf":[{"type":"null"},{"format":"date-time","type":"string"}],"description":"When the current billing period ends. The next charge is due then,\nunless a cancellation is booked for it.\n","example":"2026-11-01T10:04:00Z"},"customer_id":{"description":"Who is subscribed. A Summerbase id, or any source identity for them like\n`stripe:cus_123`, which creates the customer if it is new.\n","example":"backend:acct_4821","type":"string"},"ended_at":{"anyOf":[{"type":"null"},{"format":"date-time","type":"string"}],"description":"When service stopped, beside `status: canceled`. Clear it, with `status`\nback to `active`, to resume a subscription; the same id may be brought\nback as often as the source does so.\n"},"identities":{"description":"What other sources call this subscription, as `source:id` — `[\"stripe:cus_123\"]`. One that already identifies another subscription is refused. An identity is kept for as long as the subscription is and is never erased, so never use personal data, such as an email address, as one.\n\nWhat each source calls this record, as `<source>:<id in that source>`. Any entry addresses it — `stripe:cus_123`.","items":{"type":"string"},"type":"array"},"items":{"description":"Every item of the subscription. Sending this replaces the set.","example":[{"id":"backend:item_9001","interval":"month","price_id":"backend:growth-monthly","quantity":"25","unit_amount":"50.00"}],"items":{"$ref":"#/components/schemas/SubscriptionItemInput"},"type":"array"},"on_warning":{"description":"What to do about the parts of this write that cannot be applied as\nsent.\n\n`warn` applies everything it can and reports the rest in `warnings`, so\none unusable property never costs you a subscription's amounts. `error`\napplies none of it unless all of it applies: the write is rolled back\nand every warning comes back as an error instead.\n\nSend `error` from a form, where somebody is waiting to be told what to\nfix. Leave it alone in an ingest client, which wants the revenue to\nland either way.\n","enum":["warn","error"],"type":"string"},"properties":{"additionalProperties":{"anyOf":[{"type":"string"},{"type":"number"},{"type":"boolean"},{"$ref":"#/components/schemas/PropertyInput"}]},"description":"Properties to record in your source.\n\nProperty keys and the values to set. Only the keys named here change; `null` clears one.","type":"object"},"status":{"description":"Where it is in its life. Not whether it has been paid for.","enum":["pending","trialing","active","paused","canceled"],"example":"active","type":"string"},"trial_ends_at":{"anyOf":[{"type":"null"},{"format":"date-time","type":"string"}],"description":"When the trial ends, or ended. A subscription that trials starts\ntrialing, so when it began is `started_at` and its status history.\n"}},"required":["customer_id"],"type":"object"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UpdateSubscriptionResult"}}},"description":"Returns the subscription object, created if the id was new, with any warnings in `response_metadata`."},"default":{"$ref":"#/components/responses/Error"}},"summary":"Update a subscription","tags":["Subscriptions"]}},"/v1/subscriptions/{id}/reimport":{"post":{"callbacks":{},"deprecated":false,"description":"Replace everything your source has said about a subscription with its\nhistory, sent all at once and oldest first — the one way to write the\npast.\n\nEach entry is a patch onto the one before it, applied through the same\nrules as an update. The first is applied to a subscription holding\nnothing of yours: if your source owns it, every field the first entry\nleaves out takes what a new subscription would hold, so it has to carry\n`status` and `currency_code`, and an item takes its defaults the first\ntime the history names it. Your properties on it are replaced. Other\nsources' properties and every identity stay; `identities` adds to them.\nLands whole or not at all.\n\nWhat it replaces is deleted for good: gone from every answer within\nmoments, and from every copy Summerbase keeps within a day.\n\nA subscription that does not exist yet is created by its first entry,\nwhich takes `customer_id` and `claim_ownership` like a create does.\n","operationId":"reimport_subscription","parameters":[{"description":"The subscription — a Summerbase id, or a source identity like `stripe:sub_123`.","example":"backend:sub_7731","in":"path","name":"id","required":true,"schema":{"maxLength":320,"minLength":1,"type":"string"}},{"description":"Relationships to include in the response instead of fetching separately. A dotted path expands what an expanded record can expand in turn — at most 4 deep, 10 paths a request, and never back to a record already on the path.","in":"query","name":"expand[]","required":false,"schema":{"items":{"enum":["currency","customer","items","owner_source","customer.country","customer.erasures","items.price","owner_source.current_import","customer.erasures.requested_by","items.price.owner_source","items.price.plan","items.price.owner_source.current_import"],"type":"string"},"maxItems":10,"type":"array"}},{"description":"The organization to work within. Optional for an API key, which is already scoped to one; naming a different one is refused.","in":"header","name":"Summerbase-Organization","required":false,"schema":{"format":"uuid","type":"string"}},{"description":"The project to work within. Its organization is implied, so this alone is enough. Optional for an API key, which is already scoped to one.","in":"header","name":"Summerbase-Project","required":false,"schema":{"format":"uuid","type":"string"}}],"requestBody":{"content":{"application/json":{"example":{"claim_ownership":true,"customer_id":"backend:acct_4821","history":[{"currency_code":"USD","effective_at":"2025-11-17T10:04:00Z","items":[{"id":"backend:item_9001","interval":"month","price_id":"backend:growth-monthly","quantity":"20","unit_amount":"50.00"}],"status":"trialing","trial_ends_at":"2025-12-01T10:04:00Z"},{"effective_at":"2025-12-01T10:04:00Z","status":"active"},{"effective_at":"2026-06-12T15:47:00Z","items":[{"id":"backend:item_9001","interval":"month","price_id":"backend:growth-monthly","quantity":"25","unit_amount":"50.00"}]}]},"schema":{"additionalProperties":false,"properties":{"claim_ownership":{"description":"For a subscription that does not exist yet: `true` to create it as the\nsource it lives in. Required to create one, as on a create.\n","example":true,"type":"boolean"},"customer_id":{"description":"Who is subscribed, for a subscription that does not exist yet. A\nSummerbase id, or any source identity like `stripe:cus_123`, which\ncreates the customer if it is new.\n","example":"backend:acct_4821","type":"string"},"history":{"description":"The subscription's history, oldest first. At most 10,000 entries.","example":[{"currency_code":"USD","effective_at":"2025-11-17T10:04:00Z","items":[{"id":"backend:item_9001","interval":"month","price_id":"backend:growth-monthly","quantity":"20","unit_amount":"50.00"}],"status":"trialing","trial_ends_at":"2025-12-01T10:04:00Z"},{"effective_at":"2025-12-01T10:04:00Z","status":"active"},{"effective_at":"2026-06-12T15:47:00Z","items":[{"id":"backend:item_9001","interval":"month","price_id":"backend:growth-monthly","quantity":"25","unit_amount":"50.00"}]}],"items":{"additionalProperties":false,"properties":{"cancels_at":{"anyOf":[{"type":"null"},{"format":"date-time","type":"string"}],"description":"As on the subscription."},"currency_code":{"anyOf":[{"type":"null"},{"maxLength":3,"minLength":3,"type":"string"}],"description":"As on the subscription."},"current_period_ends_at":{"anyOf":[{"type":"null"},{"format":"date-time","type":"string"}],"description":"As on the subscription."},"effective_at":{"description":"When this became true. Later than the entry before it.","format":"date-time","type":"string"},"ended_at":{"anyOf":[{"type":"null"},{"format":"date-time","type":"string"}],"description":"As on the subscription."},"items":{"anyOf":[{"type":"null"},{"items":{"$ref":"#/components/schemas/SubscriptionItemInput"},"type":"array"}],"description":"Every item of the subscription from this moment. Replaces the set."},"properties":{"anyOf":[{"type":"null"},{"additionalProperties":{"anyOf":[{"type":"string"},{"type":"number"},{"type":"boolean"},{"$ref":"#/components/schemas/PropertyInput"}]},"type":"object"}],"description":"Properties in your source that changed at this moment; `null` clears one."},"status":{"anyOf":[{"type":"null"},{"enum":["pending","trialing","active","paused","canceled"],"type":"string"}],"description":"As on the subscription."},"trial_ends_at":{"anyOf":[{"type":"null"},{"format":"date-time","type":"string"}],"description":"As on the subscription."}},"required":["effective_at"],"type":"object"},"type":"array"},"identities":{"description":"What other sources call this subscription, as `source:id`. Attached once, beside\nthe history — an identity is an address rather than a value and has no\nmoment. One that already identifies another record is refused.\n\nWhat each source calls this record, as `<source>:<id in that source>`. Any entry addresses it — `stripe:cus_123`.","items":{"type":"string"},"type":"array"}},"required":["history"],"type":"object"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Subscription"}}},"description":"Returns the subscription object as its history leaves it."},"default":{"$ref":"#/components/responses/Error"}},"summary":"Reimport a subscription's history","tags":["Subscriptions"]}},"/v1/subscription_items/{id}":{"delete":{"callbacks":{},"deprecated":false,"description":"Take an item off its subscription. It is marked removed, `deleted: true`,\nrather than deleted. Only the source that owns its subscription may remove\nit.\n","operationId":"remove_subscription_item","parameters":[{"description":"Its Summerbase id, or an identity ref: a source slug and that source's\nown id for it, joined by a colon — `stripe:si_123`.\n","example":"0199a7c2-5b41-7c62-9d3e-6a1f8b2c4d97","in":"path","name":"id","required":true,"schema":{"type":"string"}},{"description":"The organization to work within. Optional for an API key, which is already scoped to one; naming a different one is refused.","in":"header","name":"Summerbase-Organization","required":false,"schema":{"format":"uuid","type":"string"}},{"description":"The project to work within. Its organization is implied, so this alone is enough. Optional for an API key, which is already scoped to one.","in":"header","name":"Summerbase-Project","required":false,"schema":{"format":"uuid","type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"additionalProperties":false,"properties":{"deleted":{"enum":[true],"type":"boolean"},"id":{"format":"uuid","type":"string"},"object":{"enum":["subscription_item"],"type":"string"}},"required":["id","object","deleted"],"type":"object"}}},"description":"Returns the removed subscription item's id."},"default":{"$ref":"#/components/responses/Error"}},"summary":"Remove a subscription item","tags":["Subscription items"]},"get":{"callbacks":{},"deprecated":false,"description":"Fetch one subscription item, by Summerbase id or by any source's id for it.","operationId":"get_subscription_item","parameters":[{"description":"Its Summerbase id, or an identity ref: a source slug and that source's\nown id for it, joined by a colon — `stripe:si_123`.\n","example":"0199a7c2-5b41-7c62-9d3e-6a1f8b2c4d97","in":"path","name":"id","required":true,"schema":{"type":"string"}},{"description":"Relationships to include in the response instead of fetching separately. A dotted path expands what an expanded record can expand in turn — at most 4 deep, 10 paths a request, and never back to a record already on the path.","in":"query","name":"expand[]","required":false,"schema":{"items":{"enum":["price","subscription","price.owner_source","price.plan","subscription.currency","subscription.customer","subscription.owner_source","price.owner_source.current_import","subscription.customer.country","subscription.customer.erasures","subscription.owner_source.current_import","subscription.customer.erasures.requested_by"],"type":"string"},"maxItems":10,"type":"array"}},{"description":"The organization to work within. Optional for an API key, which is already scoped to one; naming a different one is refused.","in":"header","name":"Summerbase-Organization","required":false,"schema":{"format":"uuid","type":"string"}},{"description":"The project to work within. Its organization is implied, so this alone is enough. Optional for an API key, which is already scoped to one.","in":"header","name":"Summerbase-Project","required":false,"schema":{"format":"uuid","type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SubscriptionItem"}}},"description":"Returns the subscription item object."},"default":{"$ref":"#/components/responses/Error"}},"summary":"Get a subscription item","tags":["Subscription items"]},"patch":{"callbacks":{},"deprecated":false,"description":"Change one item. Only the fields you mention change.\n\nIts own fields — unit amount, quantity, interval — are the owning source's.\nAny source may attach `properties` and `identities`.\n\nA value you change or clear is kept in the item's history. To remove one\nfor good, reimport the subscription without it, or erase a property.\n","operationId":"update_subscription_item","parameters":[{"description":"Its Summerbase id, or an identity ref: a source slug and that source's\nown id for it, joined by a colon — `stripe:si_123`.\n","example":"backend:item_9001","in":"path","name":"id","required":true,"schema":{"type":"string"}},{"description":"Relationships to include in the response instead of fetching separately. A dotted path expands what an expanded record can expand in turn — at most 4 deep, 10 paths a request, and never back to a record already on the path.","in":"query","name":"expand[]","required":false,"schema":{"items":{"enum":["price","subscription","price.owner_source","price.plan","subscription.currency","subscription.customer","subscription.owner_source","price.owner_source.current_import","subscription.customer.country","subscription.customer.erasures","subscription.owner_source.current_import","subscription.customer.erasures.requested_by"],"type":"string"},"maxItems":10,"type":"array"}},{"description":"The organization to work within. Optional for an API key, which is already scoped to one; naming a different one is refused.","in":"header","name":"Summerbase-Organization","required":false,"schema":{"format":"uuid","type":"string"}},{"description":"The project to work within. Its organization is implied, so this alone is enough. Optional for an API key, which is already scoped to one.","in":"header","name":"Summerbase-Project","required":false,"schema":{"format":"uuid","type":"string"}}],"requestBody":{"content":{"application/json":{"example":{"quantity":"30","subscription_id":"backend:sub_7731"},"schema":{"additionalProperties":false,"properties":{"deleted":{"description":"Whether the item has come off the subscription. **When** it did is the\n`effective_at` of the write that set this.\n","type":"boolean"},"discount_amount":{"description":"What comes off this item's invoice, in the subscription's currency,\nafter every discount the source applies to it has been worked out:\ncoupons at any level, stacked in the source's order, restricted to\nthe products they cover. The item is worth `unit_amount × quantity`\nless this, each interval. Zero is no discount.\n","format":"decimal","type":"string"},"discount_ends_at":{"anyOf":[{"type":"null"},{"format":"date-time","type":"string"}],"description":"When the discount, or the earliest part of it, stops applying, if the\nsource knows. Information for a reader: the discount comes off when the\nsource writes a smaller `discount_amount`, not when this passes.\n"},"identities":{"description":"What other sources call this subscription item, as `source:id` — `[\"stripe:cus_123\"]`. One that already identifies another subscription item is refused. An identity is kept for as long as the subscription item is and is never erased, so never use personal data, such as an email address, as one.\n\nWhat each source calls this record, as `<source>:<id in that source>`. Any entry addresses it — `stripe:cus_123`.","items":{"type":"string"},"type":"array"},"interval":{"description":"The unit this item bills in.","enum":["day","week","month","year"],"type":"string"},"interval_count":{"description":"How many intervals between charges.","minimum":1,"type":"integer"},"on_warning":{"description":"What to do about the parts of this write that cannot be applied as\nsent.\n\n`warn` applies everything it can and reports the rest in `warnings`, so\none unusable property never costs you a subscription's amounts. `error`\napplies none of it unless all of it applies: the write is rolled back\nand every warning comes back as an error instead.\n\nSend `error` from a form, where somebody is waiting to be told what to\nfix. Leave it alone in an ingest client, which wants the revenue to\nland either way.\n","enum":["warn","error"],"type":"string"},"price_id":{"anyOf":[{"type":"null"},{"format":"uuid","type":"string"}],"description":"The price this item is on, when a source named one."},"pricing_model":{"description":"How the unit amount is arrived at.","enum":["fixed","tiered","metered"],"type":"string"},"properties":{"additionalProperties":{"anyOf":[{"type":"string"},{"type":"number"},{"type":"boolean"},{"$ref":"#/components/schemas/PropertyInput"}]},"description":"Properties to record in your source.\n\nProperty keys and the values to set. Only the keys named here change; `null` clears one.","type":"object"},"quantity":{"description":"How many units at this unit amount. A decimal, because a unit is not\nalways a whole seat: a billing system that meters usage sends 1.5 of\nsomething, and rounding it would misstate what the item is worth.\n","example":"30","format":"decimal","type":"string"},"subscription_id":{"description":"The subscription to add it to — a Summerbase id, or a source identity.","example":"backend:sub_7731","type":"string"},"unit_amount":{"description":"What one unit costs per interval. Excludes tax and any discount.","format":"decimal","type":"string"}},"required":["subscription_id"],"type":"object"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UpdateSubscriptionItemResult"}}},"description":"Returns the subscription item object, created if the id was new, with any warnings in `response_metadata`."},"default":{"$ref":"#/components/responses/Error"}},"summary":"Update a subscription item","tags":["Subscription items"]}},"/v1/subscription_items":{"post":{"callbacks":{},"deprecated":false,"description":"Adds an item to a subscription, without resending the whole set. Your source\nmust be the one that owns the subscription.\n\nNaming one of your own ids creates the item under that name.\n","operationId":"create_subscription_item","parameters":[{"description":"Relationships to include in the response instead of fetching separately. A dotted path expands what an expanded record can expand in turn — at most 4 deep, 10 paths a request, and never back to a record already on the path.","in":"query","name":"expand[]","required":false,"schema":{"items":{"enum":["price","subscription","price.owner_source","price.plan","subscription.currency","subscription.customer","subscription.owner_source","price.owner_source.current_import","subscription.customer.country","subscription.customer.erasures","subscription.owner_source.current_import","subscription.customer.erasures.requested_by"],"type":"string"},"maxItems":10,"type":"array"}},{"description":"The organization to work within. Optional for an API key, which is already scoped to one; naming a different one is refused.","in":"header","name":"Summerbase-Organization","required":false,"schema":{"format":"uuid","type":"string"}},{"description":"The project to work within. Its organization is implied, so this alone is enough. Optional for an API key, which is already scoped to one.","in":"header","name":"Summerbase-Project","required":false,"schema":{"format":"uuid","type":"string"}}],"requestBody":{"content":{"application/json":{"example":{"interval":"month","price_id":"0198f1a4-2e5f-7b80-a1c2-3d4e5f6a7b8c","quantity":"5","subscription_id":"backend:sub_7731","unit_amount":"20.00"},"schema":{"additionalProperties":false,"properties":{"deleted":{"description":"Whether the item has come off the subscription. **When** it did is the\n`effective_at` of the write that set this.\n","type":"boolean"},"discount_amount":{"description":"What comes off this item's invoice, in the subscription's currency,\nafter every discount the source applies to it has been worked out:\ncoupons at any level, stacked in the source's order, restricted to\nthe products they cover. The item is worth `unit_amount × quantity`\nless this, each interval. Zero is no discount.\n","format":"decimal","type":"string"},"discount_ends_at":{"anyOf":[{"type":"null"},{"format":"date-time","type":"string"}],"description":"When the discount, or the earliest part of it, stops applying, if the\nsource knows. Information for a reader: the discount comes off when the\nsource writes a smaller `discount_amount`, not when this passes.\n"},"interval":{"description":"The unit this item bills in.","enum":["day","week","month","year"],"example":"month","type":"string"},"interval_count":{"description":"How many intervals between charges.","minimum":1,"type":"integer"},"on_warning":{"description":"What to do about the parts of this write that cannot be applied as\nsent.\n\n`warn` applies everything it can and reports the rest in `warnings`, so\none unusable property never costs you a subscription's amounts. `error`\napplies none of it unless all of it applies: the write is rolled back\nand every warning comes back as an error instead.\n\nSend `error` from a form, where somebody is waiting to be told what to\nfix. Leave it alone in an ingest client, which wants the revenue to\nland either way.\n","enum":["warn","error"],"type":"string"},"price_id":{"anyOf":[{"type":"null"},{"format":"uuid","type":"string"}],"description":"The price this item is on, when a source named one.","example":"0198f1a4-2e5f-7b80-a1c2-3d4e5f6a7b8c"},"pricing_model":{"description":"How the unit amount is arrived at.","enum":["fixed","tiered","metered"],"type":"string"},"properties":{"additionalProperties":{"anyOf":[{"type":"string"},{"type":"number"},{"type":"boolean"},{"$ref":"#/components/schemas/PropertyInput"}]},"description":"Properties to record in your source.\n\nProperty keys and the values to set. Only the keys named here change; `null` clears one.","type":"object"},"quantity":{"description":"How many units at this unit amount. A decimal, because a unit is not\nalways a whole seat: a billing system that meters usage sends 1.5 of\nsomething, and rounding it would misstate what the item is worth.\n","example":"5","format":"decimal","type":"string"},"subscription_id":{"description":"The subscription to add it to — a Summerbase id, or a source identity.","example":"backend:sub_7731","type":"string"},"unit_amount":{"description":"What one unit costs per interval. Excludes tax and any discount.","example":"20.00","format":"decimal","type":"string"}},"required":["subscription_id"],"type":"object"}}},"required":true},"responses":{"201":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateSubscriptionItemResult"}}},"description":"Returns the new subscription item object, with any warnings in `response_metadata`."},"default":{"$ref":"#/components/responses/Error"}},"summary":"Create a subscription item","tags":["Subscription items"]}},"/v1/plans":{"get":{"callbacks":{},"deprecated":false,"description":"The plans of the project you are working in.","operationId":"list_plans","parameters":[{"description":"Only archived plans, or only offered ones. Both, by default.","in":"query","name":"archived","required":false,"schema":{"type":"boolean"}},{"description":"The order.","in":"query","name":"sort","required":false,"schema":{"description":"The order: a key, `-` before it for descending, commas between up to 3 keys — `-created_at`, `name`, `-mrr,name`. The keys are `created_at`, `name`. Text sorts ignoring case; a record with no value for the key comes last, whichever way the list runs.","type":"string"}},{"description":"How many records to return, 1 to 100. Defaults to 20.","in":"query","name":"limit","required":false,"schema":{"default":20,"maximum":100,"minimum":1,"type":"integer"}},{"description":"The `next_cursor` of the previous page, to read the page after it.","in":"query","name":"cursor","required":false,"schema":{"type":"string"}},{"description":"Whether to add `total_count`, how many records match altogether. On the first page only: it is the same number on every page of one list, so keep the first page's, and it is refused beside a `cursor`.","in":"query","name":"include_total_count","required":false,"schema":{"default":false,"type":"boolean"}},{"description":"Relationships to include in the response instead of fetching separately. A dotted path expands what an expanded record can expand in turn — at most 4 deep, 10 paths a request, and never back to a record already on the path.","in":"query","name":"expand[]","required":false,"schema":{"items":{"enum":["prices","prices.owner_source","prices.owner_source.current_import"],"type":"string"},"maxItems":10,"type":"array"}},{"description":"The organization to work within. Optional for an API key, which is already scoped to one; naming a different one is refused.","in":"header","name":"Summerbase-Organization","required":false,"schema":{"format":"uuid","type":"string"}},{"description":"The project to work within. Its organization is implied, so this alone is enough. Optional for an API key, which is already scoped to one.","in":"header","name":"Summerbase-Project","required":false,"schema":{"format":"uuid","type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"additionalProperties":false,"description":"A page of results.","properties":{"data":{"items":{"$ref":"#/components/schemas/Plan"},"type":"array"},"has_more":{"description":"Whether another page follows this one.","example":false,"type":"boolean"},"next_cursor":{"anyOf":[{"type":"null"},{"description":"Pass back as `cursor` to fetch the next page. Null on the last page.","type":"string"}]},"object":{"enum":["list"],"type":"string"},"total_count":{"description":"How many records match altogether, counting every page. Only present when `include_total_count=true` was sent, on the first page.","minimum":0,"type":"integer"}},"required":["object","data","has_more"],"type":"object"}}},"description":"Returns a list of plans."},"default":{"$ref":"#/components/responses/Error"}},"summary":"List plans","tags":["Plans"]},"post":{"callbacks":{},"deprecated":false,"description":"Creates a plan, for prices to be put on, named by `properties.name`, and\narchived by `properties.archived`. A billing system that makes a plan\nshould say `archived`, `true` or `false`; a plan no source says is\narchived is not.\n\nCreate a plan before the prices on it, under your own id with\n`PATCH /v1/plans/{id}`, so that each price can name it.\n","operationId":"create_plan","parameters":[{"description":"Relationships to include in the response instead of fetching separately. A dotted path expands what an expanded record can expand in turn — at most 4 deep, 10 paths a request, and never back to a record already on the path.","in":"query","name":"expand[]","required":false,"schema":{"items":{"enum":["prices","prices.owner_source","prices.owner_source.current_import"],"type":"string"},"maxItems":10,"type":"array"}},{"description":"The organization to work within. Optional for an API key, which is already scoped to one; naming a different one is refused.","in":"header","name":"Summerbase-Organization","required":false,"schema":{"format":"uuid","type":"string"}},{"description":"The project to work within. Its organization is implied, so this alone is enough. Optional for an API key, which is already scoped to one.","in":"header","name":"Summerbase-Project","required":false,"schema":{"format":"uuid","type":"string"}}],"requestBody":{"content":{"application/json":{"example":{"properties":{"name":"Growth"}},"schema":{"additionalProperties":false,"properties":{"on_warning":{"description":"What to do about the parts of this write that cannot be applied as\nsent.\n\n`warn` applies everything it can and reports the rest in `warnings`, so\none unusable property never costs you a subscription's amounts. `error`\napplies none of it unless all of it applies: the write is rolled back\nand every warning comes back as an error instead.\n\nSend `error` from a form, where somebody is waiting to be told what to\nfix. Leave it alone in an ingest client, which wants the revenue to\nland either way.\n","enum":["warn","error"],"type":"string"},"properties":{"additionalProperties":{"anyOf":[{"type":"string"},{"type":"number"},{"type":"boolean"},{"$ref":"#/components/schemas/PropertyInput"}]},"description":"Properties to record in your source.\n\nProperty keys and the values to set. Only the keys named here change; `null` clears one.","example":{"name":"Growth"},"type":"object"}},"type":"object"}}},"required":false},"responses":{"201":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreatePlanResult"}}},"description":"Returns the new plan object, with any warnings in `response_metadata`."},"default":{"$ref":"#/components/responses/Error"}},"summary":"Create a plan","tags":["Plans"]}},"/v1/plans/{id}":{"get":{"callbacks":{},"deprecated":false,"description":"Fetch one plan, by Summerbase id or by any source's id for it.","operationId":"get_plan","parameters":[{"description":"The plan's Summerbase id, or an identity ref: a source slug and that\nsource's own id for it, joined by a colon — `stripe:prod_123`.\n","example":"0198f1a4-1d2e-7a40-9b31-5c6d7e8f9a0b","in":"path","name":"id","required":true,"schema":{"type":"string"}},{"description":"Relationships to include in the response instead of fetching separately. A dotted path expands what an expanded record can expand in turn — at most 4 deep, 10 paths a request, and never back to a record already on the path.","in":"query","name":"expand[]","required":false,"schema":{"items":{"enum":["prices","prices.owner_source","prices.owner_source.current_import"],"type":"string"},"maxItems":10,"type":"array"}},{"description":"The organization to work within. Optional for an API key, which is already scoped to one; naming a different one is refused.","in":"header","name":"Summerbase-Organization","required":false,"schema":{"format":"uuid","type":"string"}},{"description":"The project to work within. Its organization is implied, so this alone is enough. Optional for an API key, which is already scoped to one.","in":"header","name":"Summerbase-Project","required":false,"schema":{"format":"uuid","type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Plan"}}},"description":"Returns the plan object."},"default":{"$ref":"#/components/responses/Error"}},"summary":"Get a plan","tags":["Plans"]},"patch":{"callbacks":{},"deprecated":false,"description":"Rename a plan, or archive it. Only what you mention changes, and any\nsource may attach `properties` and `identities`.\n\nA plan is renamed with `properties.name` and archived with\n`properties.archived`, in your source: the plan takes each from the\nhighest-priority source that says one, so a source ranked below another\ndecides only where the other says nothing.\n\nArchiving is how a plan goes: it stays here, and stays on every\nsubscription that is on it, because what a customer paid for last year\ndoes not stop being true when you withdraw it.\n","operationId":"update_plan","parameters":[{"description":"The plan's Summerbase id, or an identity ref: a source slug and that\nsource's own id for it, joined by a colon — `stripe:prod_123`.\n","example":"backend:growth","in":"path","name":"id","required":true,"schema":{"type":"string"}},{"description":"Relationships to include in the response instead of fetching separately. A dotted path expands what an expanded record can expand in turn — at most 4 deep, 10 paths a request, and never back to a record already on the path.","in":"query","name":"expand[]","required":false,"schema":{"items":{"enum":["prices","prices.owner_source","prices.owner_source.current_import"],"type":"string"},"maxItems":10,"type":"array"}},{"description":"The organization to work within. Optional for an API key, which is already scoped to one; naming a different one is refused.","in":"header","name":"Summerbase-Organization","required":false,"schema":{"format":"uuid","type":"string"}},{"description":"The project to work within. Its organization is implied, so this alone is enough. Optional for an API key, which is already scoped to one.","in":"header","name":"Summerbase-Project","required":false,"schema":{"format":"uuid","type":"string"}}],"requestBody":{"content":{"application/json":{"example":{"properties":{"name":"Growth"}},"schema":{"additionalProperties":false,"properties":{"identities":{"description":"What other sources call this plan, as `source:id` — `[\"stripe:cus_123\"]`. One that already identifies another plan is refused. An identity is kept for as long as the plan is and is never erased, so never use personal data, such as an email address, as one.\n\nWhat each source calls this record, as `<source>:<id in that source>`. Any entry addresses it — `stripe:cus_123`.","items":{"type":"string"},"type":"array"},"on_warning":{"description":"What to do about the parts of this write that cannot be applied as\nsent.\n\n`warn` applies everything it can and reports the rest in `warnings`, so\none unusable property never costs you a subscription's amounts. `error`\napplies none of it unless all of it applies: the write is rolled back\nand every warning comes back as an error instead.\n\nSend `error` from a form, where somebody is waiting to be told what to\nfix. Leave it alone in an ingest client, which wants the revenue to\nland either way.\n","enum":["warn","error"],"type":"string"},"properties":{"additionalProperties":{"anyOf":[{"type":"string"},{"type":"number"},{"type":"boolean"},{"$ref":"#/components/schemas/PropertyInput"}]},"description":"Properties to record in your source.\n\nProperty keys and the values to set. Only the keys named here change; `null` clears one.","example":{"name":"Growth"},"type":"object"}},"type":"object"}}},"required":false},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UpdatePlanResult"}}},"description":"Returns the plan object, created if the id was new, with any warnings in `response_metadata`."},"default":{"$ref":"#/components/responses/Error"}},"summary":"Update a plan","tags":["Plans"]}},"/v1/plans/{id}/merge":{"post":{"callbacks":{},"deprecated":false,"description":"Merge other plans into this one: their prices, identities and properties\nmove here, and they are deleted. Every chart by plan then reads their\nprices under this plan, as far back as they go.\n\nIt cannot be undone. Moving a price back onto a plan of its own is a\n`PATCH` of the price.\n","operationId":"merge_plans","parameters":[{"description":"The plan's Summerbase id, or an identity ref: a source slug and that\nsource's own id for it, joined by a colon — `stripe:prod_123`.\n","example":"backend:growth","in":"path","name":"id","required":true,"schema":{"type":"string"}},{"description":"Relationships to include in the response instead of fetching separately. A dotted path expands what an expanded record can expand in turn — at most 4 deep, 10 paths a request, and never back to a record already on the path.","in":"query","name":"expand[]","required":false,"schema":{"items":{"enum":["prices","prices.owner_source","prices.owner_source.current_import"],"type":"string"},"maxItems":10,"type":"array"}},{"description":"The organization to work within. Optional for an API key, which is already scoped to one; naming a different one is refused.","in":"header","name":"Summerbase-Organization","required":false,"schema":{"format":"uuid","type":"string"}},{"description":"The project to work within. Its organization is implied, so this alone is enough. Optional for an API key, which is already scoped to one.","in":"header","name":"Summerbase-Project","required":false,"schema":{"format":"uuid","type":"string"}}],"requestBody":{"content":{"application/json":{"example":{"merged_plan_ids":["stripe:prod_QhR2sT4uV6wX8y"]},"schema":{"additionalProperties":false,"properties":{"merged_plan_ids":{"description":"The plans to merge into this one, which are deleted — each a Summerbase\nid, or a source identity.\n","example":["stripe:prod_QhR2sT4uV6wX8y"],"items":{"maxLength":320,"minLength":1,"type":"string"},"type":"array"}},"required":["merged_plan_ids"],"type":"object"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Plan"}}},"description":"Returns the plan the others were merged into."},"default":{"$ref":"#/components/responses/Error"}},"summary":"Merge plans into another","tags":["Plans"]}},"/v1/plans/find_duplicates":{"post":{"callbacks":{},"deprecated":false,"description":"Groups of plans that match by `fields` and so may be one plan known\nmore than once — to look over, and merge with `POST\n/v1/plans/{id}/merge`. Plans linked through a chain of matches are one\ngroup.\n","operationId":"find_duplicate_plans","parameters":[{"description":"The organization to work within. Optional for an API key, which is already scoped to one; naming a different one is refused.","in":"header","name":"Summerbase-Organization","required":false,"schema":{"format":"uuid","type":"string"}},{"description":"The project to work within. Its organization is implied, so this alone is enough. Optional for an API key, which is already scoped to one.","in":"header","name":"Summerbase-Project","required":false,"schema":{"format":"uuid","type":"string"}}],"requestBody":{"content":{"application/json":{"example":{"fields":["plan.name","plan.name"],"limit":100},"schema":{"additionalProperties":false,"properties":{"cursor":{"description":"The `next_cursor` of the previous page, to read the page after it.","type":"string"},"fields":{"description":"The two fields to compare, as `GET /v1/fields` lists them: a plan's\nIDs from one source, `plan.external_ids.stripe`, or a plan field\nholding text, `plan.name`. A plan matches another when one of its\nvalues under the first is one of the other's under the second, and\nthe two may be the same field. With an ID on either side the values\nare compared exactly; otherwise trimmed and ignoring case.\n","example":["plan.name","plan.name"],"items":{"type":"string"},"type":"array"},"include_total_count":{"default":false,"description":"Whether to add `total_count`, how many records match altogether. On the first page only: it is the same number on every page of one list, so keep the first page's, and it is refused beside a `cursor`.","type":"boolean"},"limit":{"default":20,"description":"How many records to return, 1 to 100. Defaults to 20.","example":100,"maximum":100,"minimum":1,"type":"integer"}},"required":["fields"],"type":"object"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"additionalProperties":false,"description":"A page of results.","properties":{"data":{"items":{"$ref":"#/components/schemas/DuplicatePlans"},"type":"array"},"has_more":{"description":"Whether another page follows this one.","example":false,"type":"boolean"},"next_cursor":{"anyOf":[{"type":"null"},{"description":"Pass back as `cursor` to fetch the next page. Null on the last page.","type":"string"}]},"object":{"enum":["list"],"type":"string"},"total_count":{"description":"How many records match altogether, counting every page. Only present when `include_total_count=true` was sent, on the first page.","minimum":0,"type":"integer"}},"required":["object","data","has_more"],"type":"object"}}},"description":"Returns a list of the groups of plans that match."},"default":{"$ref":"#/components/responses/Error"}},"summary":"Find duplicate plans","tags":["Plans"]}},"/v1/prices":{"get":{"callbacks":{},"deprecated":false,"description":"The prices of the project you are working in.","operationId":"list_prices","parameters":[{"description":"Only the prices on this plan — a Summerbase id, or a source identity.","in":"query","name":"plan_id","required":false,"schema":{"type":"string"}},{"description":"Only archived prices, or only offered ones. Both, by default.","in":"query","name":"archived","required":false,"schema":{"type":"boolean"}},{"description":"The order.","in":"query","name":"sort","required":false,"schema":{"description":"The order: a key, `-` before it for descending, commas between up to 3 keys — `-created_at`, `name`, `-mrr,name`. The keys are `created_at`, `name`. Text sorts ignoring case; a record with no value for the key comes last, whichever way the list runs.","type":"string"}},{"description":"How many records to return, 1 to 100. Defaults to 20.","in":"query","name":"limit","required":false,"schema":{"default":20,"maximum":100,"minimum":1,"type":"integer"}},{"description":"The `next_cursor` of the previous page, to read the page after it.","in":"query","name":"cursor","required":false,"schema":{"type":"string"}},{"description":"Whether to add `total_count`, how many records match altogether. On the first page only: it is the same number on every page of one list, so keep the first page's, and it is refused beside a `cursor`.","in":"query","name":"include_total_count","required":false,"schema":{"default":false,"type":"boolean"}},{"description":"Relationships to include in the response instead of fetching separately. A dotted path expands what an expanded record can expand in turn — at most 4 deep, 10 paths a request, and never back to a record already on the path.","in":"query","name":"expand[]","required":false,"schema":{"items":{"enum":["owner_source","plan","owner_source.current_import"],"type":"string"},"maxItems":10,"type":"array"}},{"description":"The organization to work within. Optional for an API key, which is already scoped to one; naming a different one is refused.","in":"header","name":"Summerbase-Organization","required":false,"schema":{"format":"uuid","type":"string"}},{"description":"The project to work within. Its organization is implied, so this alone is enough. Optional for an API key, which is already scoped to one.","in":"header","name":"Summerbase-Project","required":false,"schema":{"format":"uuid","type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"additionalProperties":false,"description":"A page of results.","properties":{"data":{"items":{"$ref":"#/components/schemas/Price"},"type":"array"},"has_more":{"description":"Whether another page follows this one.","example":false,"type":"boolean"},"next_cursor":{"anyOf":[{"type":"null"},{"description":"Pass back as `cursor` to fetch the next page. Null on the last page.","type":"string"}]},"object":{"enum":["list"],"type":"string"},"total_count":{"description":"How many records match altogether, counting every page. Only present when `include_total_count=true` was sent, on the first page.","minimum":0,"type":"integer"}},"required":["object","data","has_more"],"type":"object"}}},"description":"Returns a list of prices."},"default":{"$ref":"#/components/responses/Error"}},"summary":"List prices","tags":["Prices"]},"post":{"callbacks":{},"deprecated":false,"description":"Creates a price. Your source owns it, and is the only one that may rename\nit, archive it or change its terms.\n\nIts terms are what your catalog lists it at, for reading: a subscription\nitem on it is valued by the item's own terms, which may differ.\n\nA price has to exist before a subscription item names it: send it with\n`PATCH /v1/prices/{id}`, under your own id, first.\n","operationId":"create_price","parameters":[{"description":"Relationships to include in the response instead of fetching separately. A dotted path expands what an expanded record can expand in turn — at most 4 deep, 10 paths a request, and never back to a record already on the path.","in":"query","name":"expand[]","required":false,"schema":{"items":{"enum":["owner_source","plan","owner_source.current_import"],"type":"string"},"maxItems":10,"type":"array"}},{"description":"The organization to work within. Optional for an API key, which is already scoped to one; naming a different one is refused.","in":"header","name":"Summerbase-Organization","required":false,"schema":{"format":"uuid","type":"string"}},{"description":"The project to work within. Its organization is implied, so this alone is enough. Optional for an API key, which is already scoped to one.","in":"header","name":"Summerbase-Project","required":false,"schema":{"format":"uuid","type":"string"}}],"requestBody":{"content":{"application/json":{"example":{"name":"Growth monthly","plan_id":"backend:growth"},"schema":{"additionalProperties":false,"properties":{"archived":{"description":"Whether the price has stopped being offered. Existing subscriptions keep\nit — what somebody paid for last year does not stop being true when you\nwithdraw the price.\n","type":"boolean"},"currency_code":{"anyOf":[{"type":"null"},{"maxLength":3,"minLength":3,"type":"string"}],"description":"The currency it is listed in, by its ISO 4217 code."},"interval":{"anyOf":[{"type":"null"},{"enum":["day","week","month","year"],"type":"string"}],"description":"The unit it bills in. None for a price charged once."},"interval_count":{"anyOf":[{"type":"null"},{"minimum":1,"type":"integer"}],"description":"How many intervals between charges."},"name":{"anyOf":[{"type":"null"},{"maxLength":255,"minLength":1,"type":"string"}],"description":"What the price is called in the source it lives in, when it has a name\nof its own there — a Stripe price's nickname. Most have none, and are\ntold apart by their terms.\n","example":"Growth monthly"},"on_warning":{"description":"What to do about the parts of this write that cannot be applied as\nsent.\n\n`warn` applies everything it can and reports the rest in `warnings`, so\none unusable property never costs you a subscription's amounts. `error`\napplies none of it unless all of it applies: the write is rolled back\nand every warning comes back as an error instead.\n\nSend `error` from a form, where somebody is waiting to be told what to\nfix. Leave it alone in an ingest client, which wants the revenue to\nland either way.\n","enum":["warn","error"],"type":"string"},"plan_id":{"description":"The plan the price is on — a Summerbase id, or a source identity such as\nthe product it is a price of, `stripe:prod_123`. It has to exist: create\nit first, with `PATCH /v1/plans/{id}`.\n","example":"backend:growth","maxLength":320,"minLength":1,"type":"string"},"pricing_model":{"anyOf":[{"type":"null"},{"enum":["fixed","tiered","metered"],"type":"string"}],"description":"How an item's unit amount is arrived at on this price."},"properties":{"additionalProperties":{"anyOf":[{"type":"string"},{"type":"number"},{"type":"boolean"},{"$ref":"#/components/schemas/PropertyInput"}]},"description":"Properties to record in your source.\n\nProperty keys and the values to set. Only the keys named here change; `null` clears one.","type":"object"},"tax_behavior":{"description":"Whether the amount has tax in it: `inclusive`, `exclusive`, or\n`unspecified` when the price leaves it to the source's tax settings. A\nsubscription item is worth its amount without any tax in it, at the\nrate its customer's invoices are taxed at, whatever this says.\n","enum":["inclusive","exclusive","unspecified"],"type":"string"},"unit_amount":{"anyOf":[{"type":"null"},{"format":"decimal","type":"string"}],"description":"What one unit is listed at per interval: for a `metered` price, one unit\nof usage. None for a `tiered` one, whose units cost what their tier says.\nBelow zero for a credit, which Stripe, for one, lists as a one-time\nprice with a negative amount. Includes tax when `tax_behavior` says so.\n"}},"required":["plan_id"],"type":"object"}}},"required":true},"responses":{"201":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreatePriceResult"}}},"description":"Returns the new price object, with any warnings in `response_metadata`."},"default":{"$ref":"#/components/responses/Error"}},"summary":"Create a price","tags":["Prices"]}},"/v1/prices/{id}":{"get":{"callbacks":{},"deprecated":false,"description":"Fetch one price, by Summerbase id or by any source's id for it.","operationId":"get_price","parameters":[{"description":"The price's Summerbase id, or an identity ref: a source slug and that\nsource's own id for it, joined by a colon — `stripe:price_123`.\n","example":"0198f1a4-2e5f-7b80-a1c2-3d4e5f6a7b8c","in":"path","name":"id","required":true,"schema":{"type":"string"}},{"description":"Relationships to include in the response instead of fetching separately. A dotted path expands what an expanded record can expand in turn — at most 4 deep, 10 paths a request, and never back to a record already on the path.","in":"query","name":"expand[]","required":false,"schema":{"items":{"enum":["owner_source","plan","owner_source.current_import"],"type":"string"},"maxItems":10,"type":"array"}},{"description":"The organization to work within. Optional for an API key, which is already scoped to one; naming a different one is refused.","in":"header","name":"Summerbase-Organization","required":false,"schema":{"format":"uuid","type":"string"}},{"description":"The project to work within. Its organization is implied, so this alone is enough. Optional for an API key, which is already scoped to one.","in":"header","name":"Summerbase-Project","required":false,"schema":{"format":"uuid","type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Price"}}},"description":"Returns the price object."},"default":{"$ref":"#/components/responses/Error"}},"summary":"Get a price","tags":["Prices"]},"patch":{"callbacks":{},"deprecated":false,"description":"Rename a price, change its terms, archive it, or move it to another\nplan. Only what you mention changes. Only the source the price lives\nin may rename it, archive it or change its terms; any source may move\nit, and attach `properties` and `identities`.\n\nArchiving is how a price goes: it stays here, and stays on every\nsubscription that is on it, because what a customer paid for last year\ndoes not stop being true when you withdraw it.\n\nMoving a price to another plan moves its history with it: every chart\nby plan reads the price under its new plan, as far back as it goes.\n","operationId":"update_price","parameters":[{"description":"The price's Summerbase id, or an identity ref: a source slug and that\nsource's own id for it, joined by a colon — `stripe:price_123`.\n","example":"backend:growth-monthly","in":"path","name":"id","required":true,"schema":{"type":"string"}},{"description":"Relationships to include in the response instead of fetching separately. A dotted path expands what an expanded record can expand in turn — at most 4 deep, 10 paths a request, and never back to a record already on the path.","in":"query","name":"expand[]","required":false,"schema":{"items":{"enum":["owner_source","plan","owner_source.current_import"],"type":"string"},"maxItems":10,"type":"array"}},{"description":"The organization to work within. Optional for an API key, which is already scoped to one; naming a different one is refused.","in":"header","name":"Summerbase-Organization","required":false,"schema":{"format":"uuid","type":"string"}},{"description":"The project to work within. Its organization is implied, so this alone is enough. Optional for an API key, which is already scoped to one.","in":"header","name":"Summerbase-Project","required":false,"schema":{"format":"uuid","type":"string"}}],"requestBody":{"content":{"application/json":{"example":{"name":"Growth monthly","plan_id":"backend:growth"},"schema":{"additionalProperties":false,"properties":{"archived":{"description":"Whether the price has stopped being offered. Existing subscriptions keep\nit — what somebody paid for last year does not stop being true when you\nwithdraw the price.\n","type":"boolean"},"currency_code":{"anyOf":[{"type":"null"},{"maxLength":3,"minLength":3,"type":"string"}],"description":"The currency it is listed in, by its ISO 4217 code."},"identities":{"description":"What other sources call this price, as `source:id` — `[\"stripe:cus_123\"]`. One that already identifies another price is refused. An identity is kept for as long as the price is and is never erased, so never use personal data, such as an email address, as one.\n\nWhat each source calls this record, as `<source>:<id in that source>`. Any entry addresses it — `stripe:cus_123`.","items":{"type":"string"},"type":"array"},"interval":{"anyOf":[{"type":"null"},{"enum":["day","week","month","year"],"type":"string"}],"description":"The unit it bills in. None for a price charged once."},"interval_count":{"anyOf":[{"type":"null"},{"minimum":1,"type":"integer"}],"description":"How many intervals between charges."},"name":{"anyOf":[{"type":"null"},{"maxLength":255,"minLength":1,"type":"string"}],"description":"What the price is called in the source it lives in, when it has a name\nof its own there — a Stripe price's nickname. Most have none, and are\ntold apart by their terms.\n","example":"Growth monthly"},"on_warning":{"description":"What to do about the parts of this write that cannot be applied as\nsent.\n\n`warn` applies everything it can and reports the rest in `warnings`, so\none unusable property never costs you a subscription's amounts. `error`\napplies none of it unless all of it applies: the write is rolled back\nand every warning comes back as an error instead.\n\nSend `error` from a form, where somebody is waiting to be told what to\nfix. Leave it alone in an ingest client, which wants the revenue to\nland either way.\n","enum":["warn","error"],"type":"string"},"plan_id":{"description":"The plan to move the price to — a Summerbase id, or a source identity.","example":"backend:growth","maxLength":320,"minLength":1,"type":"string"},"pricing_model":{"anyOf":[{"type":"null"},{"enum":["fixed","tiered","metered"],"type":"string"}],"description":"How an item's unit amount is arrived at on this price."},"properties":{"additionalProperties":{"anyOf":[{"type":"string"},{"type":"number"},{"type":"boolean"},{"$ref":"#/components/schemas/PropertyInput"}]},"description":"Properties to record in your source.\n\nProperty keys and the values to set. Only the keys named here change; `null` clears one.","type":"object"},"tax_behavior":{"description":"Whether the amount has tax in it: `inclusive`, `exclusive`, or\n`unspecified` when the price leaves it to the source's tax settings. A\nsubscription item is worth its amount without any tax in it, at the\nrate its customer's invoices are taxed at, whatever this says.\n","enum":["inclusive","exclusive","unspecified"],"type":"string"},"unit_amount":{"anyOf":[{"type":"null"},{"format":"decimal","type":"string"}],"description":"What one unit is listed at per interval: for a `metered` price, one unit\nof usage. None for a `tiered` one, whose units cost what their tier says.\nBelow zero for a credit, which Stripe, for one, lists as a one-time\nprice with a negative amount. Includes tax when `tax_behavior` says so.\n"}},"type":"object"}}},"required":false},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UpdatePriceResult"}}},"description":"Returns the price object, created if the id was new, with any warnings in `response_metadata`."},"default":{"$ref":"#/components/responses/Error"}},"summary":"Update a price","tags":["Prices"]}},"/v1/property_definitions":{"get":{"callbacks":{},"deprecated":false,"description":"The property keys seen in the project you are working in, what each one\nholds, and which source they came from.\n","operationId":"list_property_definitions","parameters":[{"description":"Only the keys describing this kind of record.","in":"query","name":"subject_type","required":false,"schema":{"enum":["customer","subscription","subscription_item","plan","price"],"type":"string"}},{"description":"Only archived keys, or only current ones. Both, by default.","in":"query","name":"archived","required":false,"schema":{"type":"boolean"}},{"description":"The order.","in":"query","name":"sort","required":false,"schema":{"description":"The order: a key, `-` before it for descending, commas between up to 3 keys — `-created_at`, `name`, `-mrr,name`. The keys are `created_at`, `key`, `label`. Text sorts ignoring case; a record with no value for the key comes last, whichever way the list runs.","type":"string"}},{"description":"How many records to return, 1 to 100. Defaults to 20.","in":"query","name":"limit","required":false,"schema":{"default":20,"maximum":100,"minimum":1,"type":"integer"}},{"description":"The `next_cursor` of the previous page, to read the page after it.","in":"query","name":"cursor","required":false,"schema":{"type":"string"}},{"description":"Whether to add `total_count`, how many records match altogether. On the first page only: it is the same number on every page of one list, so keep the first page's, and it is refused beside a `cursor`.","in":"query","name":"include_total_count","required":false,"schema":{"default":false,"type":"boolean"}},{"description":"Relationships to include in the response instead of fetching separately. A dotted path expands what an expanded record can expand in turn — at most 4 deep, 10 paths a request, and never back to a record already on the path.","in":"query","name":"expand[]","required":false,"schema":{"items":{"enum":["source","source.current_import"],"type":"string"},"maxItems":10,"type":"array"}},{"description":"The organization to work within. Optional for an API key, which is already scoped to one; naming a different one is refused.","in":"header","name":"Summerbase-Organization","required":false,"schema":{"format":"uuid","type":"string"}},{"description":"The project to work within. Its organization is implied, so this alone is enough. Optional for an API key, which is already scoped to one.","in":"header","name":"Summerbase-Project","required":false,"schema":{"format":"uuid","type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"additionalProperties":false,"description":"A page of results.","properties":{"data":{"items":{"$ref":"#/components/schemas/PropertyDefinition"},"type":"array"},"has_more":{"description":"Whether another page follows this one.","example":false,"type":"boolean"},"next_cursor":{"anyOf":[{"type":"null"},{"description":"Pass back as `cursor` to fetch the next page. Null on the last page.","type":"string"}]},"object":{"enum":["list"],"type":"string"},"total_count":{"description":"How many records match altogether, counting every page. Only present when `include_total_count=true` was sent, on the first page.","minimum":0,"type":"integer"}},"required":["object","data","has_more"],"type":"object"}}},"description":"Returns a list of the project's property definitions."},"default":{"$ref":"#/components/responses/Error"}},"summary":"List property definitions","tags":["Property definitions"]},"post":{"callbacks":{},"deprecated":false,"description":"Declare a key before anything writes it, so the first value arrives with a\ntype already decided.\n\nWorth doing for a key whose values come from a provider, where everything\nis a string: declaring `seats` a number here is what makes a range filter\non it work later. The key lands in your own source's namespace.\n","operationId":"create_property_definition","parameters":[{"description":"Relationships to include in the response instead of fetching separately. A dotted path expands what an expanded record can expand in turn — at most 4 deep, 10 paths a request, and never back to a record already on the path.","in":"query","name":"expand[]","required":false,"schema":{"items":{"enum":["source","source.current_import"],"type":"string"},"maxItems":10,"type":"array"}},{"description":"The organization to work within. Optional for an API key, which is already scoped to one; naming a different one is refused.","in":"header","name":"Summerbase-Organization","required":false,"schema":{"format":"uuid","type":"string"}},{"description":"The project to work within. Its organization is implied, so this alone is enough. Optional for an API key, which is already scoped to one.","in":"header","name":"Summerbase-Project","required":false,"schema":{"format":"uuid","type":"string"}}],"requestBody":{"content":{"application/json":{"example":{"key":"seats","label":"Seats","subject_type":"customer","value_type":"number"},"schema":{"additionalProperties":false,"properties":{"key":{"description":"The key, as written. Immutable once it exists.","example":"seats","maxLength":255,"minLength":1,"type":"string"},"label":{"description":"What a screen calls this property: the name the source that owns it\ngave it, or the key in words — `deal_stage` is `Deal Stage`.\n","example":"Seats","maxLength":255,"minLength":1,"type":"string"},"subject_type":{"description":"What kind of record this key describes.","enum":["customer","subscription","subscription_item","plan","price"],"example":"customer","type":"string"},"value_type":{"description":"What values of this key hold, fixed by the first one written.","enum":["string","number","boolean","datetime"],"example":"number","type":"string"}},"required":["key","subject_type","value_type"],"type":"object"}}},"required":true},"responses":{"201":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/PropertyDefinition"}}},"description":"Returns the new property definition object."},"default":{"$ref":"#/components/responses/Error"}},"summary":"Create a property definition","tags":["Property definitions"]}},"/v1/property_definitions/{id}":{"get":{"callbacks":{},"deprecated":false,"description":"Fetch one property definition.","operationId":"get_property_definition","parameters":[{"description":"The definition's Summerbase id.","example":"0198f1a5-7a8b-7c9d-8e0f-1a2b3c4d5e6f","in":"path","name":"id","required":true,"schema":{"format":"uuid","type":"string"}},{"description":"Relationships to include in the response instead of fetching separately. A dotted path expands what an expanded record can expand in turn — at most 4 deep, 10 paths a request, and never back to a record already on the path.","in":"query","name":"expand[]","required":false,"schema":{"items":{"enum":["source","source.current_import"],"type":"string"},"maxItems":10,"type":"array"}},{"description":"The organization to work within. Optional for an API key, which is already scoped to one; naming a different one is refused.","in":"header","name":"Summerbase-Organization","required":false,"schema":{"format":"uuid","type":"string"}},{"description":"The project to work within. Its organization is implied, so this alone is enough. Optional for an API key, which is already scoped to one.","in":"header","name":"Summerbase-Project","required":false,"schema":{"format":"uuid","type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/PropertyDefinition"}}},"description":"Returns the property definition object."},"default":{"$ref":"#/components/responses/Error"}},"summary":"Get a property definition","tags":["Property definitions"]},"patch":{"callbacks":{},"deprecated":false,"description":"Relabel a key, or archive it. Only the source that registered the key may.\n\nArchiving is how a key goes: the values stay and still read back, and a\nscreen stops offering it. A key's type cannot be changed.\n","operationId":"update_property_definition","parameters":[{"description":"The definition's Summerbase id.","example":"0198f1a5-7a8b-7c9d-8e0f-1a2b3c4d5e6f","in":"path","name":"id","required":true,"schema":{"format":"uuid","type":"string"}},{"description":"Relationships to include in the response instead of fetching separately. A dotted path expands what an expanded record can expand in turn — at most 4 deep, 10 paths a request, and never back to a record already on the path.","in":"query","name":"expand[]","required":false,"schema":{"items":{"enum":["source","source.current_import"],"type":"string"},"maxItems":10,"type":"array"}},{"description":"The organization to work within. Optional for an API key, which is already scoped to one; naming a different one is refused.","in":"header","name":"Summerbase-Organization","required":false,"schema":{"format":"uuid","type":"string"}},{"description":"The project to work within. Its organization is implied, so this alone is enough. Optional for an API key, which is already scoped to one.","in":"header","name":"Summerbase-Project","required":false,"schema":{"format":"uuid","type":"string"}}],"requestBody":{"content":{"application/json":{"example":{"label":"Seats purchased"},"schema":{"additionalProperties":false,"properties":{"archived":{"description":"Whether the key has stopped being offered. Its values stay and still read back.","type":"boolean"},"label":{"description":"What a screen calls this property: the name the source that owns it\ngave it, or the key in words — `deal_stage` is `Deal Stage`.\n","example":"Seats purchased","maxLength":255,"minLength":1,"type":"string"}},"type":"object"}}},"required":false},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/PropertyDefinition"}}},"description":"Returns the updated property definition object."},"default":{"$ref":"#/components/responses/Error"}},"summary":"Update a property definition","tags":["Property definitions"]}}},"security":[{"bearerAuth":[]}],"servers":[{"url":"https://api.summerbase.com","variables":{}}],"tags":[{"description":"MRR, the Monthly Run Rate, ARR, churn, retention and the rest,\ncomputed from your customers and subscriptions. A query names the\nmeasures it wants — `mrr`, `arr`, `customer_churn` — and gets each\nnow, over time and by any breakdown; list the measures available, and\nthe customers behind any value.","name":"Metrics"},{"description":"A customer is a company or a person that pays you, or might. The record is\nresolved from what every source says about them, and carries the amounts\ncomputed from their subscriptions, such as `mrr` and `status`. The changes\nto those subscriptions are the run rate movements.","name":"Customers","x-object":"Customer"},{"description":"A subscription is what a customer is subscribed to, in the one source it\nlives in. It is made of items that carry the prices, and its\n`monthly_amount` is what they are worth a month; a customer's `mrr` is the\nsum over their subscriptions.","name":"Subscriptions","x-object":"Subscription"},{"description":"A subscription item is one price on a subscription: a price, a quantity and a\nunit amount, billed every interval. Its plan is its price's. A subscription's amounts are summed from\nits items. Items are usually written with their subscription, as its\n`items`, and can also be changed one at a time.","name":"Subscription items","x-object":"SubscriptionItem"},{"description":"A plan is one thing you sell — Pro, Team — and the prices it is billed as:\nmonthly and annual, one per currency, and the prices of the same plan in\nevery billing system you have used. Metrics can be broken down and filtered\nby plan. A plan is created with the first price that names it, or ahead of\ntime, and plans can be renamed and merged without touching the\nsubscriptions on them.","name":"Plans","x-object":"Plan"},{"description":"A price is what a subscription item is on, in the source it lives in: a\nStripe price, or a plan in a billing system that has no prices. Every\nprice is on one plan, which groups the prices of one thing you sell —\nmonthly and annual, one per currency, or the same plan in two billing\nsystems. A price is created before any subscription item names it.","name":"Prices","x-object":"Price"},{"description":"A run rate movement is one change to what a subscription item is worth: a new\ncustomer, an expansion, a contraction, a churn or a reactivation. The\nmetrics report's revenue values add up from these, and a customer's rows\nthat share a `counts_at` make up one movement.","name":"Run rate movements","x-object":"RunRateMovement"},{"description":"An integration is a system the project is connected to: a billing\nprovider such as Stripe, which Summerbase fetches from itself, your own\nbackend, which writes through the API, or Summerbase, where what your\nteam writes by hand is kept.\n\nAn integration that sends Summerbase data is a **source**, and\n`is_source` says whether it is one. Every customer, subscription and plan is\nidentified within a source, as `stripe:cus_123`; everything an API key\nwrites is attributed to the key's source; and wherever a record names a\nsource — `owner_source_id`, `properties_by_source` — it names one of these.\nEvery provider there is today is a source.","name":"Integrations","x-object":"Integration"},{"description":"A property definition says what a property key holds: the kind of record it\ndescribes, the type of its values, and the source that writes it. A key\nregisters itself when it is first written; declare it ahead of that to\nsettle its type first.","name":"Property definitions","x-object":"PropertyDefinition"},{"description":"A field is a key a filter's comparison or a breakdown's dimension may name:\na field a record carries, a property a source wrote, or a dimension of the\nrun rate movements themselves. Which a query takes is the query's to say:\na measure lists them in its `filter_keys` and `dimensions`.","name":"Fields","x-object":"Field"},{"description":"An organization is a company using Summerbase. It holds the company's\nprojects, and the people who work in them, each through a membership.","name":"Organizations","x-object":"Organization"},{"description":"A project holds one product's revenue data — its integrations, customers,\nsubscriptions and plans — and the settings its metrics are computed with. An\nAPI key belongs to one project, and works in it alone.","name":"Projects","x-object":"Project"},{"description":"A membership is one person's place in an organization, with the role that\ndecides what they may do there.","name":"Memberships","x-object":"Membership"},{"description":"An invitation asks somebody, by email address, to join the organization with\na role. They become a member by accepting it through the link it sends,\nwhich works for seven days.","name":"Invitations","x-object":"Invitation"},{"description":"An API key lets a program call the Summerbase API on behalf of one\nproject, with a role in it. A key that writes is bound to a source, which\neverything it writes is attributed to. Its secret is shown once, when it is\ncreated.","name":"API keys","x-object":"ApiKey"},{"description":"A user is a person with a Summerbase account. What they may do in an\norganization is the role on their membership of it. A request made with an\nAPI key is made as nobody, so it has no user.","name":"Users","x-object":"User"},{"description":"A currency Summerbase holds exchange rates for, by its ISO 4217 code: what a\nsubscription may bill in and a project may report in.","name":"Currencies","x-object":"Currency"},{"description":"A country, by its ISO 3166-1 alpha-2 code, as a customer's `country_code`\nholds it.","name":"Countries","x-object":"Country"},{"description":"A timezone a project's reporting or a metrics query can be read in, by its\nIANA name.","name":"Timezones","x-object":"Timezone"}]}