Skip to content

Where people and agents ship software together. The open-source git platform for the whole job: issues, agents, checks and deploys to the edge.

4,095 linesCodeBlameRaw
1//! The billing service: what a workspace's usage costs, its plan, credit,
2//! limits and invoices.
3//!
4//! Money is held in millionths of a US dollar, so that a sandbox run
5//! costing a fraction of a cent is recorded exactly.
6//!
7//! Each `*Args` struct is the argument of the method of the same name,
8//! served at `POST /rpc/<method>`.
9
10use serde::{Deserialize, Serialize};
11
12use crate::repos::RepoPath;
13use crate::{User, Viewer};
14
15/// Millionths of a US dollar in one dollar.
16pub const MICROS_PER_DOLLAR: i64 = 1_000_000;
17
18/// Whether workspaces are charged for agents at all, and with real money.
19/// `status` takes nothing and returns this.
20#[derive(Clone, Copy, Debug, Default, Serialize, Deserialize)]
21pub struct Status {
22 /// False when no payment provider is configured: nothing is charged,
23 /// and who may run agents is decided some other way.
24 pub enabled: bool,
25 /// False while the payment provider is in its test mode, where cards
26 /// are not real.
27 pub live: bool,
28 /// True while g1t is being built out: runs are recorded, with what
29 /// they cost, but nothing is charged and no credit is needed. Not a
30 /// promise that it stays free.
31 #[serde(default)]
32 pub free: bool,
33}
34
35/// `trial`: a workspace's trial credit, so people can try g1t (its agents on
36/// g1t's hosted models among it) without a key or a card of their own. Each
37/// new workspace gets one grant of usage credit (`TRIAL_WORKSPACE_MICROS`),
38/// made when it first uses something, out of a pool for everyone that
39/// resets each calendar month (`TRIAL_MONTHLY_POOL_MICROS`). When this
40/// month's pool is given out, new grants wait for the next month. Returns
41/// `Trial`.
42#[derive(Debug, Serialize, Deserialize)]
43#[serde(rename_all = "camelCase")]
44pub struct TrialArgs {
45 pub workspace: String,
46 /// Workspaces open to hosted models anyway, whose use is not counted
47 /// against the pool.
48 #[serde(default)]
49 pub exempt: Vec<String>,
50}
51
52#[derive(Clone, Debug, Serialize, Deserialize)]
53#[serde(rename_all = "camelCase")]
54pub struct Trial {
55 /// Whether its agents may use g1t's hosted models on the trial now: it
56 /// has credit left, or this month's pool can still grant it some.
57 pub open: bool,
58 /// What the trial has paid for so far, in millionths of a dollar.
59 pub used_micros: i64,
60 /// Its grant, or what it would be granted.
61 pub limit_micros: i64,
62 /// No longer used: the trial does not end on a date. Kept for older
63 /// readers; always null.
64 pub ends_at: Option<String>,
65 /// Why it is closed: `off` (no trials), `used` (this workspace's grant
66 /// is spent) or `pool` (this month's grants are all given out; see
67 /// `waits_until`). `ended` is no longer sent.
68 pub reason: Option<String>,
69 /// Whether the workspace has its grant already.
70 #[serde(default)]
71 pub granted: bool,
72 /// RFC 3339: when a workspace waiting for a grant can get one, the
73 /// first of next month. Only with reason `pool`.
74 #[serde(default)]
75 pub waits_until: Option<String>,
76}
77
78/// A workspace's standing.
79#[derive(Clone, Debug, Serialize, Deserialize)]
80#[serde(rename_all = "camelCase")]
81pub struct Account {
82 pub workspace: String,
83 /// Credit left, in millionths of a dollar. Can dip below zero by the
84 /// cost of the runs that were under way when it ran out.
85 pub balance_micros: i64,
86 pub status: Status,
87 /// What is added to a run's cost, in percent.
88 pub margin_percent: u32,
89 /// The card g1t charges as the workspace nears its limit and when a
90 /// month closes, if one is on file.
91 #[serde(default)]
92 pub card: Option<Card>,
93}
94
95/// A saved card, as far as it is safe to show.
96#[derive(Clone, Debug, Serialize, Deserialize)]
97#[serde(rename_all = "camelCase")]
98pub struct Card {
99 /// `visa`, `mastercard`, ...
100 pub brand: String,
101 pub last4: String,
102 pub exp_month: u32,
103 pub exp_year: u32,
104}
105
106/// `billing_portal`: Stripe's hosted billing page for the workspace, where
107/// an owner adds or replaces the card, sees invoices and receipts, and sets
108/// the billing email and address. g1t never handles card numbers. Owners
109/// only. Returns `Outcome<Checkout>` (its `url`); Stripe sends them back
110/// to `return_url`.
111#[derive(Debug, Serialize, Deserialize)]
112pub struct BillingPortalArgs {
113 pub actor: User,
114 pub workspace: String,
115 pub return_url: String,
116}
117
118/// `admin_billing_link`: for staff to send a customer: their Stripe billing
119/// page. Returns `Outcome<BillingLink>`.
120#[derive(Debug, Serialize, Deserialize)]
121pub struct AdminBillingLinkArgs {
122 pub workspace: String,
123 pub by: String,
124}
125
126#[derive(Clone, Debug, Serialize, Deserialize)]
127#[serde(rename_all = "camelCase")]
128pub struct BillingLink {
129 /// A one-time session on Stripe's billing page, signed in already.
130 pub portal_url: String,
131 /// The billing page's sign-in page, which does not expire: the
132 /// customer signs in with the email Stripe has for them.
133 pub login_url: Option<String>,
134 pub customer_email: Option<String>,
135 pub expires_note: String,
136}
137
138#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize)]
139#[serde(rename_all = "snake_case")]
140pub enum EntryKind {
141 /// Credit bought with a card.
142 TopUp,
143 /// An agent's run, or a paid feature's usage past its allowance.
144 Usage,
145}
146
147/// One line of a workspace's statement.
148#[derive(Clone, Debug, Serialize, Deserialize)]
149#[serde(rename_all = "camelCase")]
150pub struct LedgerEntry {
151 pub id: String,
152 pub kind: EntryKind,
153 /// Positive for credit added, negative for usage.
154 pub amount_micros: i64,
155 pub description: String,
156 /// For usage: the repository and pull request the agent worked on.
157 pub repo: Option<String>,
158 pub number: Option<u32>,
159 /// For usage: `implement`, `review` or `update`.
160 pub task: Option<String>,
161 /// For usage: the model, by its public name.
162 pub model: Option<String>,
163 /// For usage: `g1t` when g1t paid the model provider, `workspace` when
164 /// the workspace's own account did. Runs on the workspace's own
165 /// provider pay only their sandbox time now, so only older entries
166 /// are `workspace`.
167 #[serde(default = "g1t")]
168 pub billed_to: String,
169 /// For a top-up: the username of whoever paid.
170 pub created_by: Option<String>,
171 /// RFC 3339.
172 pub created_at: String,
173 /// The workspace the line belongs to, which tells an enterprise's
174 /// lines apart.
175 #[serde(default, skip_serializing_if = "Option::is_none")]
176 pub workspace: Option<String>,
177 /// For usage: what the g1t plan's monthly included usage paid of it.
178 /// The entry's `amount_micros` is what is left to pay.
179 #[serde(default)]
180 pub credit_micros: i64,
181 /// For usage: what the workspace's trial credit paid of it.
182 #[serde(default)]
183 pub trial_micros: i64,
184 /// For usage: what g1t's open-source pool paid of it.
185 #[serde(default)]
186 pub oss_micros: i64,
187 /// For usage: what g1t covered itself, such as the part of a free
188 /// workspace's last trial run that went past its trial credit.
189 #[serde(default)]
190 pub given_micros: i64,
191 /// For usage: what a complimentary allowance from g1t paid of it.
192 #[serde(default)]
193 pub gift_micros: i64,
194 /// For usage: what the account's discount took off its price. The
195 /// price is `-amount_micros` plus this and what paid for it.
196 #[serde(default)]
197 pub discount_micros: i64,
198 /// For a credit from g1t, and for what of one expired or was revoked:
199 /// its kind.
200 #[serde(default, skip_serializing_if = "Option::is_none")]
201 pub credit_kind: Option<CreditKind>,
202}
203
204fn g1t() -> String {
205 "g1t".to_owned()
206}
207
208/// `account` (`Outcome<Account>`) and `ledger` (`Outcome<Vec<LedgerEntry>>`,
209/// newest first). Members of the workspace only.
210#[derive(Debug, Serialize, Deserialize)]
211pub struct AccountArgs {
212 pub workspace: String,
213 pub viewer: Viewer,
214}
215
216/// `checkout`: prepays usage: money paid in advance, drawn down by usage
217/// after the plan's included usage, which raises what can be used before
218/// work stops by the same amount at once. $25 at the least. By card, with
219/// 3-D Secure; from $1,000 also by bank transfer. Owners of the workspace
220/// only. Returns `Outcome<Checkout>`.
221#[derive(Debug, Serialize, Deserialize)]
222#[serde(rename_all = "camelCase")]
223pub struct CheckoutArgs {
224 pub actor: User,
225 pub workspace: String,
226 /// How much to prepay, in cents.
227 pub amount_cents: u32,
228 /// Where the payment page sends the person afterwards. The payment's
229 /// id is appended as `session`.
230 pub return_url: String,
231 /// `card` (the default) or `bank_transfer` (from $1,000): Stripe gives
232 /// the account details, and the money counts once it arrives.
233 #[serde(default)]
234 pub method: Option<String>,
235}
236
237#[derive(Debug, Serialize, Deserialize)]
238pub struct Checkout {
239 /// The payment page to send the person to.
240 pub url: String,
241}
242
243/// `confirm`: credits a payment once the provider says it was made. Safe
244/// to call any number of times. Returns `Outcome<Account>`.
245#[derive(Debug, Serialize, Deserialize)]
246pub struct ConfirmArgs {
247 pub workspace: String,
248 pub viewer: Viewer,
249 /// The payment's id, as returned to `return_url`.
250 pub session: String,
251}
252
253/// `usage`: what a workspace's agents cost over a period, broken down.
254/// Members only. Returns `Outcome<Usage>`.
255#[derive(Debug, Serialize, Deserialize)]
256pub struct UsageArgs {
257 pub workspace: String,
258 pub viewer: Viewer,
259 /// RFC 3339: the start of the period. The period runs to now.
260 pub since: String,
261}
262
263/// One slice of usage: what it was for, what it cost, how many runs.
264#[derive(Clone, Debug, Serialize, Deserialize)]
265#[serde(rename_all = "camelCase")]
266pub struct UsageSlice {
267 pub key: String,
268 pub micros: i64,
269 pub runs: u32,
270}
271
272/// What a workspace's agents cost over a period.
273#[derive(Clone, Debug, Serialize, Deserialize)]
274#[serde(rename_all = "camelCase")]
275pub struct Usage {
276 pub since: String,
277 /// Charged, including g1t's margin.
278 pub spent_micros: i64,
279 /// What g1t's usage came to at price, less what was charged: the plan's
280 /// included usage, the trial, a pool or a free period paid it. Usage at
281 /// price is `spent_micros` plus this.
282 #[serde(default)]
283 pub covered_micros: i64,
284 /// What the account's discount took off the price. Usage at price is
285 /// `spent_micros` plus `covered_micros` plus this.
286 #[serde(default)]
287 pub discount_micros: i64,
288 /// The account's discount now, in percent; absent without one. With
289 /// one, the slices measure usage at price.
290 #[serde(default)]
291 pub discount_percent: Option<u32>,
292 /// Usage at price: `spent_micros` plus `covered_micros` plus
293 /// `discount_micros`, from the same ledger lines. The one figure every
294 /// page shows as usage (mission control, the agent fleet, Usage and
295 /// Billing), labelled "usage at price".
296 #[serde(default)]
297 pub price_micros: i64,
298 /// What g1t's model provider charged, before the margin.
299 pub cost_micros: i64,
300 /// What runs on the workspace's own provider cost there, as the harness
301 /// estimated it. Not charged by g1t.
302 pub provider_micros: i64,
303 /// What the runs used, at cost: g1t's models and the workspace's own
304 /// provider together, whatever was charged for them.
305 pub used_micros: i64,
306 /// g1t charges nothing for now. The slices then measure usage at cost,
307 /// since every charge is zero.
308 pub free: bool,
309 pub runs: u32,
310 /// Spend per day (`YYYY-MM-DD`) and task, as `day/task` keys.
311 pub by_day: Vec<UsageSlice>,
312 /// Per task: implement, review, revise, update, plan.
313 pub by_task: Vec<UsageSlice>,
314 /// Per repository, `namespace/name`.
315 pub by_repo: Vec<UsageSlice>,
316 /// The pull requests that cost most, as `namespace/name#number`.
317 pub by_pull: Vec<UsageSlice>,
318 /// Per model, by its public name.
319 pub by_model: Vec<UsageSlice>,
320 /// Credit bought in the period.
321 pub added_micros: i64,
322}
323
324/// What a workspace pays a monthly price for. There is one plan, `plan`
325/// ("g1t"): a flat price per workspace, never per person, with included
326/// usage each month, more private storage, and deployments. Never free:
327/// `FREE_WHILE_BUILDING` does not cover it.
328///
329/// `deployments` is not sold on its own any more: it comes with the plan.
330/// A service that asks `has_feature` for it is told whether the workspace
331/// has the plan, and a Deployments subscription bought before the change
332/// keeps working until its period ends.
333#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize)]
334#[serde(rename_all = "snake_case")]
335pub enum Feature {
336 /// The g1t plan. Older readers called it `team`.
337 #[serde(alias = "team")]
338 Plan,
339 /// Previews per pull request and production on g1t.page: part of the
340 /// plan.
341 Deployments,
342 /// Security and quality: the security suite's paid features on private
343 /// repositories. It comes with the plan; the price book's
344 /// `security_activation` is $0, and its scans are metered like
345 /// everything else.
346 Security,
347}
348
349impl Feature {
350 /// What is sold: the plan, and the Security and quality activation.
351 pub const ALL: [Feature; 2] = [Feature::Plan, Feature::Security];
352
353 pub fn as_str(self) -> &'static str {
354 match self {
355 Feature::Plan => "plan",
356 Feature::Deployments => "deployments",
357 Feature::Security => "security",
358 }
359 }
360
361 pub fn parse(name: &str) -> Option<Feature> {
362 match name {
363 "plan" | "team" => Some(Feature::Plan),
364 "deployments" => Some(Feature::Deployments),
365 "security" => Some(Feature::Security),
366 _ => None,
367 }
368 }
369
370 pub fn title(self) -> &'static str {
371 match self {
372 Feature::Plan => "g1t",
373 Feature::Deployments => "Deployments",
374 Feature::Security => "Security and quality",
375 }
376 }
377}
378
379/// What deployments cost g1t, in millionths of a dollar: fallbacks for
380/// when billing's price book cannot be read. Nothing here is an allowance:
381/// on the plan every unit is metered from the first, at cost plus the
382/// margin, and drawn from the plan's included usage before anything is
383/// charged. Projects, previews and the apps behind them are not metered at
384/// all: Cloudflare's Workers for Platforms includes far more scripts than
385/// g1t runs, so an app costs g1t only the requests and CPU it answers with.
386pub mod deployment_costs {
387 /// Workers for Platforms: $0.30 per million requests.
388 pub const MICROS_PER_MILLION_REQUESTS: i64 = 300_000;
389 /// $0.02 per million CPU milliseconds.
390 pub const MICROS_PER_MILLION_CPU_MS: i64 = 20_000;
391 /// What one second of a build's sandbox costs g1t (the standard
392 /// sandbox, `actions::STANDARD_INSTANCE`), rounded up: the price keeper
393 /// measured 14.5 on 2026-10-05, when it had 4 GiB and 8 GB of disk
394 /// (12.1225 of it memory, disk and the Durable Object), and standard-3's
395 /// 8 GiB and 16 GB (22.6825) add 10.56, for the same CPU in use. Only a
396 /// fallback: billing charges builds at the price book's `build_second`,
397 /// which the keeper keeps current.
398 pub const MICROS_PER_BUILD_SECOND: i64 = 26;
399 /// What one custom hostname costs g1t a month (Cloudflare for SaaS):
400 /// $0.10.
401 pub const MICROS_PER_DOMAIN_MONTH: i64 = 100_000;
402 /// Container apps (Docker deployments), at Cloudflare Containers' list
403 /// prices: a vCPU-second, a GiB-second of memory and a GB-second of
404 /// disk while awake, the Durable Object in front of each instance a
405 /// second while it runs, and a GB sent at the most Cloudflare charges
406 /// for one anywhere. The price book's `app_container_*` meters.
407 pub const MICROS_PER_CONTAINER_VCPU_SECOND: f64 = 20.0;
408 pub const MICROS_PER_CONTAINER_GIB_SECOND: f64 = 2.5;
409 pub const MICROS_PER_CONTAINER_DISK_GB_SECOND: f64 = 0.07;
410 pub const MICROS_PER_CONTAINER_SECOND: f64 = 1.5625;
411 pub const MICROS_PER_CONTAINER_EGRESS_GB: i64 = 50_000;
412}
413
414/// `record_sandbox`: how long one sandbox ran for a workspace, reported by
415/// the runner when it stops. Every sandbox g1t starts for a workspace
416/// (checks, the merge queue, workflow jobs) is recorded once per
417/// `reference`, with what it cost g1t (from the price book's
418/// `sandbox_second`), and charged in runner minutes: its time rounded up to
419/// the minute, at the rate of its machine (`actions::INSTANCE_TYPES`), and
420/// never under that cost. Deploy builds are charged by the Deployments plan
421/// instead.
422/// Returns `Outcome<bool>`: false if that reference was recorded before.
423#[derive(Debug, Serialize, Deserialize)]
424#[serde(rename_all = "camelCase")]
425pub struct RecordSandboxArgs {
426 pub workspace: String,
427 pub seconds: u32,
428 /// What ran, e.g. `Checks on acme/api#12`.
429 pub description: String,
430 /// `namespace/name`.
431 pub repo: Option<String>,
432 /// Unique to the run.
433 pub reference: String,
434 /// What ran: `check`, `workflow`, `queue` or `deploy`. Decides whether
435 /// g1t's open-source pool may pay for it (checks, workflows and the
436 /// merge queue on public repositories). Absent: not the pool.
437 #[serde(default)]
438 pub kind: Option<ComputeKind>,
439 /// The vCPU-seconds the sandbox used, when it can tell. With it, the
440 /// run is priced on its own CPU (`sandbox_base_second` per second plus
441 /// `sandbox_cpu_second` per vCPU-second); without it, at the average
442 /// (`sandbox_second`).
443 #[serde(default, alias = "cpu_seconds")]
444 pub cpu_seconds: Option<f64>,
445 /// The reservation the work started under, settled with this cost.
446 #[serde(default, alias = "reservation_id")]
447 pub reservation_id: Option<String>,
448 /// It ran on one of the workspace's self-hosted runners: recorded as
449 /// self-hosted time, for the minutes, at $0.
450 #[serde(default, alias = "self_hosted")]
451 pub self_hosted: bool,
452 /// The machine it ran on, by label (`g1t-4core`); absent, the standard
453 /// one. Its row in the machine catalogue sets the rate a minute and the
454 /// machine its minutes are reported under.
455 #[serde(default)]
456 pub instance: Option<String>,
457}
458
459/// How much a workspace has earned g1t's trust with money, which sets how
460/// far its unpaid usage can go before its work stops.
461#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize)]
462#[serde(rename_all = "snake_case")]
463pub enum Trust {
464 /// No live payment yet: only a little past the free allowances.
465 New,
466 /// Has paid g1t real money: the ceiling grows with what it has paid.
467 Paid,
468 /// Has paid steadily for months, with nothing disputed or declined:
469 /// the ceiling follows its monthly spend, up to $10,000, by itself.
470 Established,
471 /// A ceiling g1t set by hand, after talking to the workspace.
472 Reviewed,
473 /// g1t's own workspaces: no ceiling.
474 Internal,
475}
476
477#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize)]
478#[serde(rename_all = "snake_case")]
479pub enum LimitState {
480 Ok,
481 /// Past 80% of the ceiling.
482 Warning,
483 /// At or past it: no new sandboxes, builds or app requests.
484 Stopped,
485}
486
487/// How far a workspace's unpaid usage has gone this month, and where its
488/// work stops: like Fly's or Cloudflare's limits for new accounts, so no
489/// one runs up costs g1t cannot collect. Usage counts at what it cost g1t
490/// or what it is charged, whichever is more, so it counts while g1t is
491/// free too.
492#[derive(Clone, Debug, Serialize, Deserialize)]
493#[serde(rename_all = "camelCase")]
494pub struct Limit {
495 pub workspace: String,
496 /// The account that pays, whose usage and payments the limit counts:
497 /// the workspace's own, or its enterprise's.
498 #[serde(default)]
499 pub account: String,
500 #[serde(default)]
501 pub account_name: String,
502 pub trust: Trust,
503 /// Usage this month (UTC) less what was paid this month.
504 pub exposure_micros: i64,
505 /// Where work stops: the lower of g1t's ceiling and the owner's own
506 /// spend limit. None for g1t's own workspaces.
507 pub ceiling_micros: Option<i64>,
508 /// The ceiling g1t sets from `trust`.
509 pub trust_ceiling_micros: Option<i64>,
510 /// The owner's own monthly limit, if they set one.
511 pub spend_limit_micros: Option<i64>,
512 pub state: LimitState,
513 /// What to tell people when work is stopped or close to it.
514 pub message: Option<String>,
515 /// Charged this month, which the spend limit is measured against: the
516 /// month's usage at price, less the account's discount, what the
517 /// plan's included usage, the trial or a pool paid, and what credit
518 /// paid; usage metered through the month and charged when it closes
519 /// counted on the same terms. The one figure the Billing page's plan
520 /// card, Spend's "Charged" and the top bar show (`UsageTotals::charged_micros`
521 /// over the month is the same number).
522 #[serde(default)]
523 pub spent_micros: i64,
524 /// True while the owners have not chosen a spend limit of their own, so
525 /// the automatic one applies: $200, or twice last month's spend.
526 #[serde(default)]
527 pub default_spend_limit: bool,
528 /// The most the owners may set their own limit to: g1t's ceiling. To
529 /// go past it, they contact g1t.
530 #[serde(default)]
531 pub available_micros: Option<i64>,
532 /// How the ceiling grows from here, in a sentence.
533 #[serde(default)]
534 pub growth: Option<String>,
535 /// Money paid in advance and not used yet. It raises what can be used
536 /// before work stops by the same amount, at once.
537 #[serde(default)]
538 pub prepaid_micros: i64,
539 /// The highest ceiling the workspace has ever had. Owners may set their
540 /// spend limit anywhere up to it (plus what is prepaid) without asking.
541 #[serde(default)]
542 pub max_ceiling_micros: Option<i64>,
543 /// The most the owners may raise the limit to themselves, once, with
544 /// `raise_once`: twice the highest ceiling. None once it is used.
545 #[serde(default)]
546 pub raise_once_micros: Option<i64>,
547 /// When the one-time raise was used, RFC 3339.
548 #[serde(default)]
549 pub raised_at: Option<String>,
550 /// True in a paid workspace's first billing cycle, when the ceiling is
551 /// the starting one (`LIMIT_PAID_START_MICROS`).
552 #[serde(default)]
553 pub first_month: bool,
554 /// The budget's alerts, in percent of the spend limit: some of 50, 75,
555 /// 90 and 100. Each is emailed to the owners once a month.
556 #[serde(default)]
557 pub alert_levels: Vec<u32>,
558 /// Whether usage pauses at the spend limit (the default). Off, the
559 /// limit only alerts; g1t's own ceiling still applies.
560 #[serde(default = "yes")]
561 pub pause_at_limit: bool,
562 /// An HTTPS address told of each budget alert with a JSON POST.
563 #[serde(default)]
564 pub budget_webhook: Option<String>,
565}
566
567fn yes() -> bool {
568 true
569}
570
571/// `limit`: a workspace's limit, for its members. Returns `Outcome<Limit>`.
572#[derive(Debug, Serialize, Deserialize)]
573pub struct LimitArgs {
574 pub workspace: String,
575 pub viewer: Viewer,
576}
577
578/// `check_limit`: the same, for the services that enforce it. Returns
579/// `Outcome<Limit>`.
580#[derive(Debug, Serialize, Deserialize)]
581pub struct CheckLimitArgs {
582 pub workspace: String,
583}
584
585/// `note_pending`: usage this month that will be charged later, such as
586/// app traffic past a plan, so the workspace's limit counts it now. Each
587/// report replaces the last for that workspace, source and month. Called
588/// by the service that meters it. Returns `bool`.
589#[derive(Debug, Serialize, Deserialize)]
590#[serde(rename_all = "camelCase")]
591pub struct NotePendingArgs {
592 pub workspace: String,
593 /// `deployments`, `security` (scans), `context` (search embeddings),
594 /// `storage` or `cache` (actions/cache, plan only). Billing charges
595 /// `security`, `context`, `storage` and `cache` itself once the month
596 /// is over; `deployments` charges its own.
597 pub source: String,
598 /// What it cost g1t so far this month, before the margin.
599 pub cost_micros: i64,
600 /// How much of it, for the Billing page: `1.2 million requests and
601 /// 3.4 million CPU ms`, `2 custom domains`.
602 #[serde(default)]
603 pub detail: Option<String>,
604 /// The month (`YYYY-MM`, UTC) the total belongs to, read when the total
605 /// was worked out rather than when it arrives. Billing takes this
606 /// month, or the month before only while that month's figure for the
607 /// source is not charged and the month is not closed; anything else is
608 /// refused. None: the month the report arrives in.
609 #[serde(default, skip_serializing_if = "Option::is_none")]
610 pub month: Option<String>,
611}
612
613/// `usage_meters`: this month's usage for a workspace, one line per kind
614/// of meter, at what it is charged (cost plus the margin, on the account's
615/// terms) before the plan's included usage, the trial or g1t's pools paid
616/// for any of it. Members only. Returns `Outcome<Vec<MeterUsage>>`.
617#[derive(Debug, Serialize, Deserialize)]
618pub struct UsageMetersArgs {
619 pub workspace: String,
620 pub viewer: Viewer,
621}
622
623/// One kind of meter's usage this month.
624#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
625#[serde(rename_all = "camelCase")]
626pub struct MeterUsage {
627 /// `agents` (agent runs, models and sandboxes for checks, workflows
628 /// and the merge queue), `builds`, `requests` (app requests and CPU),
629 /// `domains`, `git_storage` (git operations and private storage) or
630 /// `search_scans` (search embeddings and security scans).
631 pub key: String,
632 pub label: String,
633 /// At price, before what paid for it.
634 pub micros: i64,
635 /// How much, when it is known: `12 runs`, `41 build minutes`.
636 #[serde(default)]
637 pub quantity: Option<String>,
638}
639
640/// `set_spend_limit`: the owner's own monthly ceiling, under g1t's; None
641/// removes it. Owners only. Returns `Outcome<Limit>`.
642#[derive(Debug, Serialize, Deserialize)]
643#[serde(rename_all = "camelCase")]
644pub struct SetSpendLimitArgs {
645 pub actor: User,
646 pub workspace: String,
647 /// A monthly limit, at most what is available; None goes back to the
648 /// default.
649 pub spend_limit_micros: Option<i64>,
650 /// Use everything available, with no limit of their own.
651 #[serde(default)]
652 pub use_full_limit: bool,
653 /// Use the one-time raise: up to twice the highest ceiling the
654 /// workspace has had, without asking. Once per workspace.
655 #[serde(default, alias = "raiseOnce")]
656 pub raise_once: bool,
657}
658
659/// One metered unit: what it costs g1t, and what it is sold at. The price
660/// is always `cost × (100 + markup) / 100`, so it follows the cost.
661#[derive(Clone, Debug, Serialize, Deserialize)]
662#[serde(rename_all = "camelCase")]
663pub struct Price {
664 /// `sandbox_second`, `build_second`, `app_requests`, `app_cpu`, `app_month`.
665 pub meter: String,
666 pub title: String,
667 pub unit: String,
668 /// Millionths of a dollar per unit; may have a fraction.
669 pub cost_micros: f64,
670 pub markup_percent: u32,
671 pub price_micros: f64,
672 /// `list`: Cloudflare's published price. `cloudflare`: what Cloudflare
673 /// actually billed g1t, measured.
674 pub source: String,
675 /// When it was last checked against Cloudflare's bill.
676 pub checked_at: Option<String>,
677 pub updated_at: String,
678}
679
680impl Price {
681 pub fn price_for(cost_micros: f64, markup_percent: u32) -> f64 {
682 cost_micros * f64::from(100 + markup_percent) / 100.0
683 }
684}
685
686/// A cost that moved.
687#[derive(Clone, Debug, Serialize, Deserialize)]
688#[serde(rename_all = "camelCase")]
689pub struct PriceChange {
690 pub meter: String,
691 pub old_cost_micros: f64,
692 pub new_cost_micros: f64,
693 pub markup_percent: u32,
694 /// The markup before, when the change was to the markup rather than
695 /// to the cost. Absent when the markup stayed `markup_percent`.
696 #[serde(default, skip_serializing_if = "Option::is_none")]
697 pub old_markup_percent: Option<u32>,
698 pub reason: String,
699 pub created_at: String,
700 /// When a change still to come takes effect: a rise is announced
701 /// before it is charged. Absent for changes already made.
702 #[serde(default, skip_serializing_if = "Option::is_none")]
703 pub effective_at: Option<String>,
704}
705
706/// `prices`: every metered price and the recent changes. Public. Returns
707/// `PriceBook`.
708#[derive(Clone, Debug, Serialize, Deserialize)]
709#[serde(rename_all = "camelCase")]
710pub struct PriceBook {
711 pub prices: Vec<Price>,
712 pub changes: Vec<PriceChange>,
713 /// The margin on model usage, which is charged at what AI Gateway
714 /// priced each request at.
715 pub model_margin_percent: u32,
716 /// Every plan, as it is sold now.
717 #[serde(default)]
718 pub plans: Vec<Plan>,
719 /// What is free, and what pays for it.
720 #[serde(default)]
721 pub free: Option<FreeTier>,
722}
723
724/// What g1t gives without a plan, each with what pays for it: a capped
725/// budget, never an open-ended allowance.
726#[derive(Clone, Debug, Default, Serialize, Deserialize)]
727#[serde(rename_all = "camelCase")]
728pub struct FreeTier {
729 /// Each new workspace's trial credit, once.
730 pub trial_workspace_micros: i64,
731 /// Trial grants each month, in all; new trials wait when it is spent.
732 pub trial_monthly_pool_micros: i64,
733 /// g1t's open-source pool each month, and any one repository's share.
734 pub oss_pool_micros: i64,
735 pub oss_repo_micros: i64,
736 /// Private repository storage that is free for every workspace. Past
737 /// it, the plan pays at cost plus the margin; a free workspace's pushes
738 /// to private repositories stop instead.
739 pub free_private_storage_bytes: i64,
740 /// Days of audit log a free workspace keeps.
741 pub audit_retention_days: u32,
742 /// Days of audit log the g1t plan keeps, and g1t's own and enterprise
743 /// workspaces. Longer is by arrangement, set per account in sudo.
744 #[serde(default)]
745 pub plan_audit_retention_days: u32,
746 /// The smallest amount a card is charged when a month closes; less
747 /// carries over. Charges at a limit always go through.
748 pub min_charge_micros: i64,
749 /// Git operations (clones, fetches and pushes through g1t) that are
750 /// free for every workspace each month. Past it, the plan pays at cost
751 /// plus the margin and is never slowed; a free workspace is slowed
752 /// down, never charged.
753 #[serde(default)]
754 pub git_operations_included: u64,
755 /// A new paid workspace's ceiling in its first month.
756 #[serde(default)]
757 pub paid_start_ceiling_micros: i64,
758 /// The most a one-click goodwill credit can cost g1t.
759 #[serde(default)]
760 pub overage_forgive_cost_micros: i64,
761}
762
763/// Who pays: a billing account. Every workspace has one; by default its
764/// own. An enterprise account pays for several workspaces at once, as
765/// GitHub Enterprise does: one bill, one limit, one set of terms.
766#[derive(Clone, Debug, Serialize, Deserialize)]
767#[serde(rename_all = "camelCase")]
768pub struct BillingAccount {
769 /// `ws_<slug>` for a workspace's own account; `ent_…` for an enterprise.
770 pub id: String,
771 pub kind: AccountKind,
772 pub name: String,
773 pub terms: Terms,
774 /// The workspaces it pays for.
775 pub workspaces: Vec<String>,
776 /// Where an enterprise's invoices go.
777 #[serde(default)]
778 pub billing_email: Option<String>,
779 /// An enterprise's invoices, newest first. Empty for a workspace's own.
780 #[serde(default)]
781 pub invoices: Vec<EnterpriseInvoice>,
782 pub created_at: String,
783 /// What g1t staff set for the account beyond its terms.
784 #[serde(default)]
785 pub allowances: Allowances,
786}
787
788/// Set per account by g1t staff in sudo, on top of its terms.
789#[derive(Clone, Debug, Default, PartialEq, Serialize, Deserialize)]
790#[serde(rename_all = "camelCase")]
791pub struct Allowances {
792 /// The g1t plan without paying for its monthly price, such as for a
793 /// partner. Usage is charged as usual. Comped accounts have it anyway.
794 #[serde(default, alias = "team")]
795 pub plan: bool,
796 /// Each of the account's public repositories' monthly cap on g1t's
797 /// open-source pool, in place of `OSS_REPO_MICROS`. None: the default.
798 #[serde(default)]
799 pub oss_repo_micros: Option<i64>,
800 /// The trial credit each of its workspaces gets, in place of
801 /// `TRIAL_WORKSPACE_MICROS`, outside the monthly pool. None: the default.
802 #[serde(default)]
803 pub trial_micros: Option<i64>,
804 /// Agents at once, in place of the plan's (2 in the first month or on
805 /// the trial, then 10). None: the default.
806 #[serde(default)]
807 pub max_concurrent_agents: Option<u32>,
808 /// Days of audit log its workspaces keep, in place of the plan's (7
809 /// free, 90 on the plan), longer or shorter. None: the plan's.
810 #[serde(default)]
811 pub audit_retention_days: Option<u32>,
812 /// A hold g1t staff put on new compute, with why. None: no hold.
813 #[serde(default)]
814 pub hold: Option<String>,
815}
816
817/// `admin_set_allowances`: the plan on or off without charge, overrides of
818/// the plan's caps, a hold, and the account's share of g1t's pools.
819/// Recorded with who and why. Returns `Outcome<BillingAccount>`.
820#[derive(Debug, Serialize, Deserialize)]
821pub struct AdminSetAllowancesArgs {
822 pub id: String,
823 pub allowances: Allowances,
824 pub note: String,
825 pub by: String,
826}
827
828// --- Entitlements, and compute started under a reservation -----------------
829//
830// Every service that starts compute (sandboxes for agents, checks,
831// workflows and the merge queue; builds; models; semantic search) asks
832// billing first:
833//
834// 1. `entitlements { workspace }` says what the workspace may do at all:
835// its plan, whether it may start compute, its caps, and whether compute
836// is paused.
837// 2. `reserve { workspace, repo, public, kind, estimate_micros }` holds the
838// work's estimated cost against what may pay for it, so that starts at
839// the same moment cannot overshoot the ceiling together. It answers who
840// pays first, or refuses with a stable code and a message for the owner.
841// 3. `settle { reservation_id, actual_micros }` releases the hold once the
842// work is done. The charge itself goes on the ledger the usual way
843// (`finish_run`, `record_sandbox`, `charge_feature`, `note_pending`).
844//
845// A reservation never settled expires after `RESERVATION_HOURS`.
846
847/// A reservation that is never settled stops holding after this long.
848pub const RESERVATION_HOURS: u64 = 3;
849/// What a ceiling reads as when there is none (g1t's own workspaces): a
850/// billion dollars, which JavaScript holds exactly.
851pub const UNLIMITED_MICROS: i64 = 1_000_000_000_000_000;
852
853/// What a workspace pays g1t on, as far as compute is concerned.
854#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize)]
855#[serde(rename_all = "snake_case")]
856pub enum PlanKind {
857 /// No plan: the forge is free; compute only from a trial or g1t's
858 /// open-source pool, after a card check.
859 Free,
860 /// The g1t plan, paid for (or given by g1t staff without its price).
861 Paid,
862 /// g1t's own workspaces and Flagon's (comped terms): the plan without
863 /// being charged. Usage is still recorded at what it cost.
864 Internal,
865 /// Paid for by an enterprise account, invoiced.
866 Enterprise,
867 /// Enrolled by g1t staff (`admin_set_complimentary`): the plan's
868 /// features, a monthly usage allowance valued at ordinary prices, and a
869 /// monthly fee of its own (often none). Never charged on demand: past
870 /// the allowance new billable work stops.
871 Complimentary,
872}
873
874impl PlanKind {
875 pub fn as_str(self) -> &'static str {
876 match self {
877 PlanKind::Free => "free",
878 PlanKind::Paid => "paid",
879 PlanKind::Internal => "internal",
880 PlanKind::Enterprise => "enterprise",
881 PlanKind::Complimentary => "complimentary",
882 }
883 }
884
885 /// Whether usage past what is included may be charged (on demand).
886 pub fn on_demand(self) -> bool {
887 !matches!(self, PlanKind::Free | PlanKind::Complimentary)
888 }
889}
890
891/// What compute is for.
892#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize)]
893#[serde(rename_all = "snake_case")]
894pub enum ComputeKind {
895 /// An agent run of g1t's own, retired: kept so stored reservations and
896 /// ledger lines parse; never issued.
897 Agent,
898 /// Checks on a pull request.
899 Check,
900 /// A workflow job.
901 Workflow,
902 /// The merge queue's checks.
903 Queue,
904 /// A deployment's build.
905 Deploy,
906 /// Semantic search embeddings, retired: kept so stored reservations
907 /// parse; never issued.
908 Embedding,
909}
910
911impl ComputeKind {
912 pub fn as_str(self) -> &'static str {
913 match self {
914 ComputeKind::Agent => "agent",
915 ComputeKind::Check => "check",
916 ComputeKind::Workflow => "workflow",
917 ComputeKind::Queue => "queue",
918 ComputeKind::Deploy => "deploy",
919 ComputeKind::Embedding => "embedding",
920 }
921 }
922
923 /// Whether g1t's open-source pool may pay for it on a public
924 /// repository: checks, workflows and the merge queue only.
925 pub fn open_source_pool(self) -> bool {
926 matches!(self, ComputeKind::Check | ComputeKind::Workflow | ComputeKind::Queue)
927 }
928}
929
930/// Who pays first for reserved work. What the first source cannot cover
931/// falls to the next, in this order.
932#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize)]
933#[serde(rename_all = "snake_case")]
934pub enum PaidBy {
935 /// The plan's included usage this month.
936 Credit,
937 /// The workspace's one-time trial credit.
938 Trial,
939 /// g1t's open-source pool.
940 Oss,
941 /// Charged to the workspace, at cost plus the margin.
942 OnDemand,
943}
944
945/// `entitlements`: what a workspace may do now, for the services that
946/// start compute and the pages that show it. Takes `EntitlementsArgs`;
947/// returns `Entitlements`. No viewer: callers decide who sees it.
948#[derive(Debug, Serialize, Deserialize)]
949pub struct EntitlementsArgs {
950 pub workspace: String,
951}
952
953/// `audit_retention`: how many days of audit log each workspace keeps, for
954/// the events service's daily purge. Takes `AuditRetentionArgs`; returns
955/// `Vec<AuditRetention>`, one for each workspace asked about.
956#[derive(Debug, Serialize, Deserialize)]
957pub struct AuditRetentionArgs {
958 pub workspaces: Vec<String>,
959}
960
961#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
962pub struct AuditRetention {
963 pub workspace: String,
964 pub days: u32,
965}
966
967#[derive(Clone, Debug, Serialize, Deserialize)]
968#[serde(rename_all = "camelCase")]
969pub struct Entitlements {
970 pub workspace: String,
971 pub plan: PlanKind,
972 /// May start sandboxes, models, deployments and semantic search at all:
973 /// paid, internal and enterprise workspaces, or a free one with trial
974 /// credit left. A free workspace may still use the open-source pool
975 /// for checks, workflows and the merge queue on public repositories
976 /// after a card check; `reserve` decides that per start.
977 pub compute: bool,
978 /// The one-time trial credit left; 0 if none was granted or it is used.
979 pub trial_micros_left: i64,
980 /// A card check has been done. The trial and the open-source pool need
981 /// it.
982 pub trial_verified: bool,
983 /// A paid workspace still in its first billing cycle.
984 pub first_month: bool,
985 /// Agents at once: 2 in the first month or on the trial, 10 after;
986 /// staff can override it.
987 pub max_concurrent_agents: u32,
988 /// The longest one run may take: 60 minutes in the first month or on
989 /// the trial; otherwise the guardrails' own caps (`MAX_MINUTES`).
990 pub max_run_minutes: u32,
991 /// Where on-demand work stops: g1t's ceiling on usage not yet paid
992 /// for. `UNLIMITED_MICROS` for g1t's own workspaces; 0 for a free one,
993 /// which has no on-demand usage.
994 pub ceiling_micros: i64,
995 /// Usage not yet paid for this month, with prepayment taken off.
996 pub exposure_micros: i64,
997 /// Why new compute is paused, for the owner: the limit is reached, a
998 /// spend spike is waiting for an owner to confirm it, or g1t staff put
999 /// a hold on it. None when it is not.
1000 pub paused: Option<String>,
1001 /// g1t staff put a hold on the account (sudo, Allowances): besides new
1002 /// compute, nothing new is set up for it, such as a self-hosted runner.
1003 #[serde(default)]
1004 pub held: bool,
1005 // What the workspace's plan gives it, for its pages.
1006 /// What open reservations hold now.
1007 #[serde(default)]
1008 pub held_micros: i64,
1009 /// Paid in advance and not used yet.
1010 #[serde(default)]
1011 pub prepaid_micros: i64,
1012 /// The plan's included usage each month, and what of it is used.
1013 #[serde(default)]
1014 pub included_micros: i64,
1015 #[serde(default)]
1016 pub included_used_micros: i64,
1017 /// How far back the audit log can be read and exported, and what is
1018 /// kept: the plan's days, or what g1t staff set for the account.
1019 pub audit_retention_days: u32,
1020 /// Whether `audit_retention_days` is what staff set for the account
1021 /// rather than the plan's.
1022 #[serde(default)]
1023 pub audit_retention_custom: bool,
1024 /// Private repository storage that is free for every workspace: past
1025 /// it, the plan pays for it and a free workspace's pushes stop.
1026 pub free_private_storage_bytes: i64,
1027 /// The last daily measure of the workspace's private repositories.
1028 pub private_storage_bytes: i64,
1029 /// On a paid plan (not Free): storage past the free amounts below is
1030 /// charged, so nothing is refused for it.
1031 #[serde(default)]
1032 pub has_plan: bool,
1033 /// Package storage free for every workspace, public and private: past
1034 /// it, the plan pays for it and a free workspace's pushes are refused.
1035 #[serde(default)]
1036 pub package_public_free_bytes: i64,
1037 #[serde(default)]
1038 pub package_private_free_bytes: i64,
1039 /// What g1t's open-source pool paid for the workspace this month.
1040 pub oss_paid_micros: i64,
1041 /// Deploy build time this month, every second of it metered.
1042 #[serde(default)]
1043 pub build_seconds_used: u32,
1044 /// Git operations this month, and how many are free for every
1045 /// workspace (past it: metered on the plan, slowed when free).
1046 #[serde(default)]
1047 pub git_operations: u64,
1048 #[serde(default)]
1049 pub git_operations_included: u64,
1050 /// The smallest amount a card is charged when a month closes.
1051 pub min_charge_micros: i64,
1052 /// A spend spike waiting for an owner, or decided.
1053 #[serde(default)]
1054 pub spike: Option<Spike>,
1055 /// Where usage stands against what is included and the limits, from 50%.
1056 #[serde(default)]
1057 pub alerts: Vec<UsageAlert>,
1058 /// A complimentary enrollment's allowance this month, when the plan is
1059 /// `complimentary` (or g1t's own with one).
1060 #[serde(default, skip_serializing_if = "Option::is_none")]
1061 pub complimentary: Option<ComplimentaryMonth>,
1062}
1063
1064/// A complimentary allowance this calendar month (UTC), as the workspace's
1065/// Billing page and sudo show it. Usage counts at its ordinary price.
1066#[derive(Clone, Debug, Default, PartialEq, Serialize, Deserialize)]
1067#[serde(rename_all = "camelCase")]
1068pub struct ComplimentaryMonth {
1069 /// `YYYY-MM`.
1070 pub month: String,
1071 /// This month's allowance; `unlimited` ignores it.
1072 pub allowance_micros: i64,
1073 #[serde(default)]
1074 pub unlimited: bool,
1075 /// Usage this month the allowance paid for.
1076 pub used_micros: i64,
1077 /// Usage this month not on the ledger yet (storage, git operations and
1078 /// other month-end meters), which the allowance will pay for.
1079 #[serde(default)]
1080 pub pending_micros: i64,
1081 /// What work running now holds against it.
1082 #[serde(default)]
1083 pub held_micros: i64,
1084 /// What is left after all three; 0 at most stops new billable work.
1085 pub remaining_micros: i64,
1086 /// What g1t covered this month past the allowance (work already
1087 /// running when it ran out): never charged to the workspace.
1088 #[serde(default)]
1089 pub overrun_micros: i64,
1090 /// When the next month's allowance starts: RFC 3339.
1091 pub resets_at: String,
1092 /// The recurring monthly fee, in cents; 0: waived.
1093 #[serde(default)]
1094 pub fee_cents: u32,
1095 /// Past the allowance, usage is charged to the workspace's own card at
1096 /// ordinary prices, under its limits; false: new paid work stops.
1097 #[serde(default)]
1098 pub charge_past_allowance: bool,
1099 /// A card is on file to charge past the allowance.
1100 #[serde(default)]
1101 pub has_card: bool,
1102}
1103
1104/// One level reached: 50, 75, 90 or 100 percent of something.
1105#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
1106#[serde(rename_all = "camelCase")]
1107pub struct UsageAlert {
1108 /// `included` (the plan's included usage), `spend_limit` (the owners'
1109 /// own limit) or `ceiling` (g1t's, on usage not yet paid for).
1110 pub meter: String,
1111 pub level: u32,
1112 pub used_micros: i64,
1113 pub limit_micros: i64,
1114 pub message: String,
1115}
1116
1117/// An hour's spend well above the workspace's usual pace: new compute
1118/// waits until an owner says to keep going.
1119#[derive(Clone, Debug, Serialize, Deserialize)]
1120#[serde(rename_all = "camelCase")]
1121pub struct Spike {
1122 pub id: String,
1123 /// `open` (waiting for an owner), `continued` (an owner said keep
1124 /// going) or `stopped` (an owner said stop).
1125 pub status: String,
1126 /// The hour's spend when it was found, and the usual hour's.
1127 pub hour_micros: i64,
1128 pub average_micros: i64,
1129 pub detected_at: String,
1130 #[serde(default)]
1131 pub decided_by: Option<String>,
1132 #[serde(default)]
1133 pub decided_at: Option<String>,
1134 /// While continued: until when, unless spend doubles again first.
1135 #[serde(default)]
1136 pub until: Option<String>,
1137}
1138
1139/// `reserve`: holds an estimate of a start's cost before the work starts.
1140/// Returns `Outcome<Reservation>`, or a failure whose code says why not:
1141///
1142/// - `paused`: a spend spike waiting for an owner, or a hold.
1143/// - `limit`: the spend limit or g1t's ceiling would be passed.
1144/// - `not_paid`: no plan, and nothing else pays for this kind of work (or
1145/// no card check yet).
1146/// - `trial_used`: the one-time trial is spent.
1147/// - `oss_pool_empty`: the open-source pool, or the repository's share of
1148/// it, is spent this month.
1149///
1150/// The message says exactly what to do, with the page to do it on (such as
1151/// `/acme/-/billing`).
1152#[derive(Debug, Serialize, Deserialize)]
1153#[serde(rename_all = "camelCase")]
1154pub struct ReserveArgs {
1155 pub workspace: String,
1156 pub repo: RepoPath,
1157 /// Whether the repository is public: the open-source pool pays only for
1158 /// public repositories' checks, workflows and merge queue.
1159 pub public: bool,
1160 pub kind: ComputeKind,
1161 /// The most the work is expected to cost g1t, before the margin, in
1162 /// millionths of a dollar (billing adds the margin, as it does to every
1163 /// charge): its sandbox for its whole time cap.
1164 #[serde(alias = "estimate_micros")]
1165 pub estimate_micros: i64,
1166}
1167
1168#[derive(Clone, Debug, Serialize, Deserialize)]
1169#[serde(rename_all = "camelCase")]
1170pub struct Reservation {
1171 pub id: String,
1172 pub paid_by: PaidBy,
1173 /// What is held, at cost; less than the estimate when a free
1174 /// workspace's last bit of trial credit is all there is.
1175 #[serde(default)]
1176 pub held_micros: i64,
1177 /// RFC 3339: when the hold lapses if never settled.
1178 #[serde(default)]
1179 pub expires_at: String,
1180}
1181
1182/// `settle`: releases a reservation's hold with what the work cost. The
1183/// charge goes on the ledger the usual way. Safe to repeat. Returns
1184/// `Outcome<bool>`: false if it was settled or had lapsed before.
1185#[derive(Debug, Serialize, Deserialize)]
1186#[serde(rename_all = "camelCase")]
1187pub struct SettleArgs {
1188 #[serde(alias = "reservation_id")]
1189 pub reservation_id: String,
1190 /// What the work cost g1t, before the margin.
1191 #[serde(alias = "actual_micros")]
1192 pub actual_micros: i64,
1193}
1194
1195// --- Card checks, the plan, prepayment -------------------------------------
1196
1197/// `card_check`: starts Stripe's page to save and verify a card: a setup
1198/// with 3-D Secure where the card supports it, which the card's bank sees
1199/// as a $0 or $1 authorization that is never charged. The trial and the
1200/// open-source pool need it, and it is the card the plan uses. Owners only.
1201/// Returns `Outcome<Checkout>`; the page's id comes back to `return_url` as
1202/// `session`, for `confirm_card_check`.
1203#[derive(Debug, Serialize, Deserialize)]
1204#[serde(rename_all = "camelCase")]
1205pub struct CardCheckArgs {
1206 pub actor: User,
1207 pub workspace: String,
1208 #[serde(alias = "return_url")]
1209 pub return_url: String,
1210}
1211
1212/// `confirm_card_check`: records the check once Stripe says the card was
1213/// verified, and grants the trial if the month's pool has room and the card
1214/// has not had one before. Safe to repeat. Returns `Outcome<Entitlements>`.
1215#[derive(Debug, Serialize, Deserialize)]
1216pub struct ConfirmCardCheckArgs {
1217 pub workspace: String,
1218 pub viewer: Viewer,
1219 pub session: String,
1220}
1221
1222// --- Limits: raising them, and spikes ---------------------------------------
1223
1224/// A request to g1t: a higher limit, or help with usage that went past
1225/// what was meant.
1226#[derive(Clone, Debug, Serialize, Deserialize)]
1227#[serde(rename_all = "camelCase")]
1228pub struct LimitRequest {
1229 pub id: String,
1230 pub workspace: String,
1231 /// `limit` (raise my limit) or `overage` (spent more than meant to).
1232 pub kind: String,
1233 /// The limit asked for; for an overage, what they think went wrong.
1234 pub amount_micros: i64,
1235 pub reason: String,
1236 pub expected_monthly_micros: i64,
1237 /// `open`, `approved` or `declined`.
1238 pub status: String,
1239 /// What was approved, which may differ from what was asked.
1240 #[serde(default)]
1241 pub decided_micros: Option<i64>,
1242 #[serde(default)]
1243 pub decided_by: Option<String>,
1244 /// The answer, as the owner sees it.
1245 #[serde(default)]
1246 pub answer: Option<String>,
1247 pub created_by: String,
1248 pub created_at: String,
1249 #[serde(default)]
1250 pub decided_at: Option<String>,
1251}
1252
1253/// `request_limit`: an owner asks g1t for more, or for help with usage past
1254/// what they meant. Answered within one business day, in the app and by
1255/// email. Owners only. Returns `Outcome<LimitRequest>`.
1256#[derive(Debug, Serialize, Deserialize)]
1257#[serde(rename_all = "camelCase")]
1258pub struct RequestLimitArgs {
1259 pub actor: User,
1260 pub workspace: String,
1261 /// `limit` or `overage`.
1262 pub kind: String,
1263 #[serde(alias = "amount_micros")]
1264 pub amount_micros: i64,
1265 pub reason: String,
1266 #[serde(default, alias = "expected_monthly_micros")]
1267 pub expected_monthly_micros: i64,
1268}
1269
1270/// `limit_requests`: a workspace's requests, newest first. Members only.
1271/// Returns `Outcome<Vec<LimitRequest>>`.
1272#[derive(Debug, Serialize, Deserialize)]
1273pub struct LimitRequestsArgs {
1274 pub workspace: String,
1275 pub viewer: Viewer,
1276}
1277
1278/// `confirm_spike`: an owner's answer to a spend spike. Keep going lifts the
1279/// pause for 24 hours, or until the hour's spend doubles again; stop keeps
1280/// new compute paused until an owner says to keep going. Owners only.
1281/// Returns `Outcome<Entitlements>`.
1282#[derive(Debug, Serialize, Deserialize)]
1283#[serde(rename_all = "camelCase")]
1284pub struct ConfirmSpikeArgs {
1285 pub actor: User,
1286 pub workspace: String,
1287 #[serde(alias = "keep_going")]
1288 pub keep_going: bool,
1289}
1290
1291/// What staff see beside a request: the workspace's history with g1t.
1292#[derive(Clone, Debug, Default, Serialize, Deserialize)]
1293#[serde(rename_all = "camelCase")]
1294pub struct WorkspaceHistory {
1295 pub plan: Option<PlanKind>,
1296 /// The last six months, oldest first.
1297 pub months: Vec<MonthFigures>,
1298 /// Live payments that have cleared, and how many.
1299 pub paid_cleared_micros: i64,
1300 pub payments: u32,
1301 pub disputes: u32,
1302 pub declines: u32,
1303 /// The first time the workspace appears in billing, RFC 3339.
1304 pub first_seen: Option<String>,
1305 pub ceiling_micros: Option<i64>,
1306 pub max_ceiling_micros: Option<i64>,
1307 pub spend_limit_micros: Option<i64>,
1308 /// Recent velocity: the last hour, the usual hour over the last week,
1309 /// and the last 24 hours, at price.
1310 pub last_hour_micros: i64,
1311 pub average_hour_micros: i64,
1312 pub last_day_micros: i64,
1313}
1314
1315#[derive(Clone, Debug, Serialize, Deserialize)]
1316#[serde(rename_all = "camelCase")]
1317pub struct LimitRequestReview {
1318 pub request: LimitRequest,
1319 pub history: WorkspaceHistory,
1320}
1321
1322/// `admin_limit_requests`: requests for staff, oldest open first. Returns
1323/// `Vec<LimitRequestReview>`.
1324#[derive(Debug, Default, Serialize, Deserialize)]
1325pub struct AdminLimitRequestsArgs {
1326 /// `open` (the default), `approved`, `declined` or `all`.
1327 #[serde(default)]
1328 pub status: Option<String>,
1329}
1330
1331/// `admin_decide_limit_request`: approve (at the amount asked, or
1332/// `amount_micros`) or decline. The owner is told in the app and by email.
1333/// Recorded with who and why. Returns `Outcome<LimitRequest>`.
1334#[derive(Debug, Serialize, Deserialize)]
1335pub struct AdminDecideLimitRequestArgs {
1336 pub id: String,
1337 /// `approve` or `decline`.
1338 pub decision: String,
1339 #[serde(default)]
1340 pub amount_micros: Option<i64>,
1341 /// What the owner is told, beside the decision.
1342 #[serde(default)]
1343 pub note: String,
1344 pub by: String,
1345}
1346
1347/// `admin_record_payment`: money that reached g1t outside the card pages,
1348/// such as a bank transfer, entered as a payment (it raises the limit like
1349/// one). Recorded with who and the transfer's reference. Returns
1350/// `Outcome<LedgerEntry>`.
1351#[derive(Debug, Serialize, Deserialize)]
1352pub struct AdminRecordPaymentArgs {
1353 pub workspace: String,
1354 pub amount_micros: i64,
1355 /// The bank's reference for the transfer, or Stripe's payment id.
1356 pub reference: String,
1357 pub note: String,
1358 pub by: String,
1359}
1360
1361// --- Complimentary enrollment (sudo) -----------------------------------------
1362//
1363// Staff enroll a workspace by hand; no workspace is ever enrolled on its
1364// own. The enrollment is the plan's features, a monthly usage allowance at
1365// ordinary prices, and a recurring monthly fee of its own, each set apart.
1366// A real Stripe customer and subscription stand for it.
1367
1368/// The most a monthly complimentary allowance can be, against a slipped
1369/// finger: $100,000. `unlimited` is how to give more.
1370pub const MAX_COMPLIMENTARY_MICROS: i64 = 100_000 * MICROS_PER_DOLLAR;
1371/// The most a complimentary enrollment's monthly fee can be: $100,000.
1372pub const MAX_COMPLIMENTARY_FEE_CENTS: u32 = 10_000_000;
1373
1374/// `admin_set_complimentary`: enrolls a workspace, or changes the terms of
1375/// its enrollment from now on. Changing the amount keeps what this month
1376/// already used; lowering it below that leaves nothing more this month and
1377/// never a debt. Recorded with who and why. Returns
1378/// `Outcome<ComplimentaryStanding>`.
1379#[derive(Debug, Serialize, Deserialize)]
1380pub struct AdminSetComplimentaryArgs {
1381 pub workspace: String,
1382 /// The monthly usage allowance, valued at ordinary prices.
1383 pub allowance_micros: i64,
1384 /// No monthly cap on the allowance (g1t's own workspaces). g1t's
1385 /// infrastructure limits still apply.
1386 #[serde(default)]
1387 pub unlimited: bool,
1388 /// The recurring monthly fee, in cents, before tax; 0 waives it.
1389 #[serde(default)]
1390 pub fee_cents: u32,
1391 /// Past the allowance, charge the workspace's own card at ordinary
1392 /// prices under its limits (an employee's or a friend's workspace g1t
1393 /// covers up to an amount); false: new paid work stops there.
1394 #[serde(default)]
1395 pub charge_past_allowance: bool,
1396 pub note: String,
1397 pub by: String,
1398}
1399
1400/// `admin_end_complimentary`: ends an enrollment now. The workspace goes
1401/// back to Free; usage the allowance paid for stays paid for. Its Stripe
1402/// subscription is canceled. Returns `Outcome<ComplimentaryStanding>`.
1403#[derive(Debug, Serialize, Deserialize)]
1404pub struct AdminEndComplimentaryArgs {
1405 pub workspace: String,
1406 pub note: String,
1407 pub by: String,
1408}
1409
1410/// `admin_complimentary`: a workspace's enrollment, if it ever had one.
1411/// Returns `Option<ComplimentaryStanding>`.
1412#[derive(Debug, Serialize, Deserialize)]
1413pub struct AdminComplimentaryArgs {
1414 pub workspace: String,
1415}
1416
1417/// A complimentary enrollment, as sudo shows it.
1418#[derive(Clone, Debug, Default, PartialEq, Serialize, Deserialize)]
1419#[serde(rename_all = "camelCase")]
1420pub struct ComplimentaryStanding {
1421 pub workspace: String,
1422 pub grant_id: String,
1423 /// `pending` (Stripe not confirmed yet), `active` or `ended`.
1424 pub status: String,
1425 pub allowance_micros: i64,
1426 #[serde(default)]
1427 pub unlimited: bool,
1428 #[serde(default)]
1429 pub fee_cents: u32,
1430 #[serde(default)]
1431 pub charge_past_allowance: bool,
1432 /// Whether its Stripe objects are live-mode ones.
1433 #[serde(default)]
1434 pub live: bool,
1435 #[serde(default)]
1436 pub customer_id: Option<String>,
1437 #[serde(default)]
1438 pub subscription_id: Option<String>,
1439 #[serde(default)]
1440 pub stripe_status: Option<String>,
1441 /// A Stripe step still owed (`create`, `terms`, `cancel`, `convert`),
1442 /// tried again every fifteen minutes.
1443 #[serde(default)]
1444 pub pending_op: Option<String>,
1445 #[serde(default)]
1446 pub last_error: Option<String>,
1447 pub note: String,
1448 pub created_by: String,
1449 pub created_at: String,
1450 pub updated_by: String,
1451 pub updated_at: String,
1452 #[serde(default)]
1453 pub ended_at: Option<String>,
1454 /// `revoked` or `converted`.
1455 #[serde(default)]
1456 pub ended_reason: Option<String>,
1457 /// This month, while it is active.
1458 #[serde(default)]
1459 pub month: Option<ComplimentaryMonth>,
1460 /// Its terms over time, newest first.
1461 #[serde(default)]
1462 pub periods: Vec<ComplimentaryPeriod>,
1463 /// Money the workspace actually paid g1t, all time: plan and fee
1464 /// invoices, payments in advance and bank transfers, net of refunds.
1465 #[serde(default)]
1466 pub money_received_micros: i64,
1467 /// Credit g1t gave it (promotional and goodwill), all time: never money.
1468 #[serde(default)]
1469 pub promotional_credit_micros: i64,
1470 /// Usage complimentary allowances paid for, all time: never money.
1471 #[serde(default)]
1472 pub gifted_micros: i64,
1473}
1474
1475/// One stretch of an enrollment's terms.
1476#[derive(Clone, Debug, Default, PartialEq, Serialize, Deserialize)]
1477#[serde(rename_all = "camelCase")]
1478pub struct ComplimentaryPeriod {
1479 pub id: String,
1480 pub starts_at: String,
1481 #[serde(default)]
1482 pub ends_at: Option<String>,
1483 pub allowance_micros: i64,
1484 #[serde(default)]
1485 pub unlimited: bool,
1486 #[serde(default)]
1487 pub fee_cents: u32,
1488 #[serde(default)]
1489 pub charge_past_allowance: bool,
1490 pub note: String,
1491 pub created_by: String,
1492}
1493
1494/// `convert_to_paid`: an owner or billing manager of a complimentary
1495/// workspace starts the g1t plan on the same Stripe customer and
1496/// subscription. The plan, and usage past the included amount, start only
1497/// once its first payment succeeds; until then the enrollment stands. Safe
1498/// to call again: a conversion waiting on the bank is answered, never
1499/// started twice. Returns `Outcome<Conversion>`.
1500#[derive(Debug, Serialize, Deserialize)]
1501#[serde(rename_all = "camelCase")]
1502pub struct ConvertToPaidArgs {
1503 pub actor: crate::User,
1504 pub workspace: String,
1505 pub return_url: String,
1506}
1507
1508/// Where a conversion stands.
1509#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
1510#[serde(rename_all = "camelCase")]
1511pub struct Conversion {
1512 /// `converted`; `needs_card` (no saved card: `url` checks one);
1513 /// `needs_action` (the bank wants the person: `url` is the invoice to
1514 /// pay); `declined` (nothing changed; the enrollment stands).
1515 pub status: String,
1516 #[serde(default)]
1517 pub url: Option<String>,
1518 pub message: String,
1519}
1520
1521// --- Overages and goodwill (sudo) --------------------------------------------
1522
1523/// What a one-time goodwill credit would come to: g1t's margin on the
1524/// overage, always, plus as much of its underlying cost as the cap allows.
1525#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, Serialize, Deserialize)]
1526#[serde(rename_all = "camelCase")]
1527pub struct Goodwill {
1528 /// This month's charges above the workspace's typical month.
1529 pub overage_micros: i64,
1530 /// The part of the overage that is g1t's margin.
1531 pub margin_micros: i64,
1532 /// The part that is what g1t paid its providers.
1533 pub cost_micros: i64,
1534 /// The one-click credit: the margin plus the cost up to the cap.
1535 pub credit_micros: i64,
1536 /// Of the credit, the real cost g1t absorbs.
1537 pub absorbed_micros: i64,
1538}
1539
1540/// A workspace whose month went well past its usual, or hit a spike.
1541#[derive(Clone, Debug, Serialize, Deserialize)]
1542#[serde(rename_all = "camelCase")]
1543pub struct Overage {
1544 pub workspace: String,
1545 pub plan: PlanKind,
1546 /// The median of its last three months' charges.
1547 pub typical_month_micros: i64,
1548 pub this_month_micros: i64,
1549 /// What this month cost g1t, and what g1t keeps of it.
1550 pub cost_micros: i64,
1551 pub margin_micros: i64,
1552 /// A spike this month, if there was one.
1553 pub spike: Option<Spike>,
1554 /// The runs that cost the most this month.
1555 pub top_entries: Vec<LedgerEntry>,
1556 pub goodwill: Goodwill,
1557 /// False when a goodwill credit was given in the last 12 months.
1558 pub goodwill_available: bool,
1559 pub last_goodwill_at: Option<String>,
1560 /// An open overage request from the owner, if there is one.
1561 pub request: Option<LimitRequest>,
1562}
1563
1564/// `admin_overages`: the Overages queue. Returns `Vec<Overage>`.
1565#[derive(Debug, Default, Serialize, Deserialize)]
1566pub struct AdminOveragesArgs {}
1567
1568/// `admin_goodwill`: credits a workspace for accidental usage. With no
1569/// amount, the one-click credit (`Goodwill::credit_micros`), once per
1570/// workspace in 12 months. A larger amount, or a second within 12 months,
1571/// needs a typed reason. It shows on the statement as "Credit from g1t:
1572/// accidental usage on <date>". Returns `Outcome<LedgerEntry>`.
1573#[derive(Debug, Serialize, Deserialize)]
1574pub struct AdminGoodwillArgs {
1575 pub workspace: String,
1576 #[serde(default)]
1577 pub amount_micros: Option<i64>,
1578 /// Why, typed by staff; needed past the one-click credit.
1579 #[serde(default)]
1580 pub reason: String,
1581 /// The day the accidental usage happened, `YYYY-MM-DD`; today if absent.
1582 #[serde(default)]
1583 pub day: Option<String>,
1584 pub by: String,
1585 /// The form's own key, made once when it is shown: the same key twice
1586 /// (a double click, a retry) gives one credit. Without it, the same
1587 /// workspace, day and amount within the same few minutes is the same
1588 /// credit.
1589 #[serde(default)]
1590 pub request_key: Option<String>,
1591}
1592
1593/// One workspace's recent pace, for sudo's velocity view.
1594#[derive(Clone, Debug, Serialize, Deserialize)]
1595#[serde(rename_all = "camelCase")]
1596pub struct Velocity {
1597 pub workspace: String,
1598 pub plan: PlanKind,
1599 pub last_hour_micros: i64,
1600 pub average_hour_micros: i64,
1601 pub last_day_micros: i64,
1602 pub this_month_micros: i64,
1603 /// The last hour over the usual hour; 0 with no history.
1604 pub ratio: f64,
1605 pub spike: Option<Spike>,
1606 pub first_seen: Option<String>,
1607}
1608
1609/// `admin_velocity`: workspaces spending in the last day, fastest first.
1610/// Returns `Vec<Velocity>`.
1611#[derive(Debug, Default, Serialize, Deserialize)]
1612pub struct AdminVelocityArgs {}
1613
1614#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize)]
1615#[serde(rename_all = "snake_case")]
1616pub enum AccountKind {
1617 Workspace,
1618 Enterprise,
1619}
1620
1621/// How an account is charged. Standard unless g1t set otherwise in sudo.
1622#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
1623#[serde(rename_all = "camelCase")]
1624pub struct Terms {
1625 pub kind: TermsKind,
1626 /// Off every usage charge, in percent. Custom terms only.
1627 #[serde(default)]
1628 pub discount_percent: u32,
1629 /// A ceiling on unpaid usage that replaces the one trust would give.
1630 #[serde(default)]
1631 pub ceiling_micros: Option<i64>,
1632 /// Why, for whoever looks next.
1633 #[serde(default)]
1634 pub note: String,
1635 /// When the terms end and the account goes back to standard.
1636 #[serde(default)]
1637 pub until: Option<String>,
1638 #[serde(default)]
1639 pub set_by: Option<String>,
1640 #[serde(default)]
1641 pub set_at: Option<String>,
1642}
1643
1644impl Terms {
1645 pub fn standard() -> Self {
1646 Terms {
1647 kind: TermsKind::Standard,
1648 discount_percent: 0,
1649 ceiling_micros: None,
1650 note: String::new(),
1651 until: None,
1652 set_by: None,
1653 set_at: None,
1654 }
1655 }
1656
1657 /// The discount in percent, 0 to 100. Terms from before discounts
1658 /// replaced "comped" read as 100%.
1659 pub fn percent_off(&self) -> u32 {
1660 match self.kind {
1661 TermsKind::Comped => 100,
1662 TermsKind::Custom => self.discount_percent.min(100),
1663 TermsKind::Standard => 0,
1664 }
1665 }
1666
1667 /// A 100% discount: nothing is charged, usage is recorded at its price
1668 /// and discounted in full. g1t's own workspaces and partners. Paid
1669 /// features are on without a plan, and g1t's own spend on it is held to
1670 /// a monthly budget (the terms' ceiling, at cost).
1671 pub fn full_discount(&self) -> bool {
1672 self.percent_off() >= 100
1673 }
1674
1675 /// What a charge becomes under these terms.
1676 pub fn apply(&self, charge_micros: i64) -> i64 {
1677 charge_micros * i64::from(100 - self.percent_off()) / 100
1678 }
1679
1680 /// What a charge at cost plus the margin becomes under these terms, and
1681 /// what the discount took off it (`ledger.discount_micros`), so the
1682 /// statement shows the usage at its price and the discount beside it,
1683 /// and a discount below cost plus the margin is counted as given, never
1684 /// lost. A 100% discount takes it all.
1685 pub fn discounted(&self, charge_micros: i64) -> (i64, i64) {
1686 let charged = self.apply(charge_micros);
1687 (charged, (charge_micros - charged).max(0))
1688 }
1689
1690 /// How the statement and sudo name the terms: `100% discount`, `30% off`.
1691 pub fn discount_label(&self) -> Option<String> {
1692 match self.percent_off() {
1693 0 => None,
1694 100 => Some("100% discount".to_owned()),
1695 percent => Some(format!("{percent}% off")),
1696 }
1697 }
1698}
1699
1700#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize)]
1701#[serde(rename_all = "snake_case")]
1702pub enum TermsKind {
1703 /// Prices as published, limits by trust.
1704 Standard,
1705 /// Before discounts: what a 100% discount is now. Read as one
1706 /// (`Terms::percent_off`); billing never writes it (migration 0039).
1707 Comped,
1708 /// A discount (up to 100%), a ceiling, or both.
1709 Custom,
1710}
1711
1712/// `stripe_webhook`: an event from Stripe, as the API received it: the raw
1713/// body and its `Stripe-Signature` header. Billing checks the signature
1714/// against the secret of the endpoint it registered, and handles each
1715/// event once. Returns `Outcome<bool>`: false for one already handled.
1716#[derive(Debug, Serialize, Deserialize)]
1717pub struct StripeWebhookArgs {
1718 pub payload: String,
1719 pub signature: String,
1720}
1721
1722/// `admin_stripe`: where billing stands with Stripe. Staff only. Returns
1723/// `StripeStatus`. With `fix: true`, first enables the destination at
1724/// billing's address and gives it the events billing needs.
1725#[derive(Debug, Default, Serialize, Deserialize)]
1726pub struct AdminStripeArgs {
1727 #[serde(default)]
1728 pub fix: bool,
1729 #[serde(default)]
1730 pub by: Option<String>,
1731}
1732
1733#[derive(Clone, Debug, Serialize, Deserialize)]
1734#[serde(rename_all = "camelCase")]
1735pub struct StripeStatus {
1736 /// `test` or `live`, from the key; `off` without one.
1737 pub mode: String,
1738 /// Whether `STRIPE_WEBHOOK_SECRET` is set, so events can be checked.
1739 pub secret_set: bool,
1740 /// The destination at billing's address in Stripe, as Stripe has it.
1741 pub webhook: Option<StripeWebhook>,
1742 /// Events billing handles that the destination does not send.
1743 pub missing_events: Vec<String>,
1744 /// The latest events handled, newest first.
1745 pub recent_events: Vec<StripeEventSummary>,
1746 /// What went wrong reading or fixing the destination, if it did.
1747 pub error: Option<String>,
1748 /// Stripe Tax for this key's mode, as Stripe has it; None when it
1749 /// could not be read (or payments are off).
1750 #[serde(default)]
1751 pub tax: Option<StripeTaxStatus>,
1752 /// The customer portal configuration billing makes (`metadata[g1t]` =
1753 /// `billing`); None until the first portal session makes it.
1754 #[serde(default)]
1755 pub portal: Option<StripePortalStatus>,
1756}
1757
1758/// Stripe Tax's settings: `active` once it can collect tax, else
1759/// `pending`, with the settings it still needs.
1760#[derive(Clone, Debug, Default, PartialEq, Serialize, Deserialize)]
1761#[serde(rename_all = "camelCase")]
1762pub struct StripeTaxStatus {
1763 pub status: String,
1764 #[serde(default)]
1765 pub missing_fields: Vec<String>,
1766}
1767
1768/// g1t's customer portal configuration at Stripe.
1769#[derive(Clone, Debug, Default, PartialEq, Serialize, Deserialize)]
1770#[serde(rename_all = "camelCase")]
1771pub struct StripePortalStatus {
1772 pub configuration_id: String,
1773 pub active: bool,
1774}
1775
1776#[derive(Clone, Debug, Serialize, Deserialize)]
1777#[serde(rename_all = "camelCase")]
1778pub struct StripeWebhook {
1779 pub url: String,
1780 pub endpoint_id: String,
1781 /// `enabled` or `disabled`.
1782 pub status: String,
1783 pub events: Vec<String>,
1784 pub created_at: String,
1785}
1786
1787#[derive(Clone, Debug, Serialize, Deserialize)]
1788#[serde(rename_all = "camelCase")]
1789pub struct StripeEventSummary {
1790 pub id: String,
1791 pub kind: String,
1792 pub outcome: String,
1793 pub received_at: String,
1794}
1795
1796/// `admin_enterprise_billing`: where an enterprise's invoices go. Creates
1797/// or updates its Stripe customer. Returns `Outcome<BillingAccount>`.
1798#[derive(Debug, Serialize, Deserialize)]
1799pub struct AdminEnterpriseBillingArgs {
1800 pub id: String,
1801 pub email: String,
1802 pub by: String,
1803}
1804
1805/// `admin_enterprise_address`: the enterprise's billing address and tax ID,
1806/// saved on its Stripe customer (made by `admin_enterprise_billing`).
1807/// Stripe Tax works its invoices' tax out from the address; an invoice is
1808/// not sent without one. Returns `Outcome<bool>`.
1809#[derive(Debug, Serialize, Deserialize)]
1810#[serde(rename_all = "camelCase")]
1811pub struct AdminEnterpriseAddressArgs {
1812 pub id: String,
1813 pub address: PostalAddress,
1814 #[serde(default, alias = "tax_id_type")]
1815 pub tax_id_type: Option<String>,
1816 #[serde(default, alias = "tax_id")]
1817 pub tax_id: Option<String>,
1818 pub by: String,
1819}
1820
1821/// `admin_invoice_enterprise`: sends an enterprise its invoice now, for
1822/// what its workspaces owe, rather than waiting for the month to close.
1823/// Returns `Outcome<EnterpriseInvoice>`.
1824#[derive(Debug, Serialize, Deserialize)]
1825pub struct AdminInvoiceEnterpriseArgs {
1826 pub id: String,
1827 pub by: String,
1828}
1829
1830/// An enterprise's invoice: one line per workspace, paid on Stripe.
1831#[derive(Clone, Debug, Serialize, Deserialize)]
1832#[serde(rename_all = "camelCase")]
1833pub struct EnterpriseInvoice {
1834 pub invoice_id: String,
1835 /// Stripe's page for it, where it is paid.
1836 pub hosted_url: Option<String>,
1837 pub amount_micros: i64,
1838 /// `open`, `paid`, `overdue` or `void`.
1839 pub status: String,
1840 pub period: String,
1841 pub lines: Vec<InvoiceLine>,
1842 pub created_at: String,
1843}
1844
1845#[derive(Clone, Debug, Serialize, Deserialize)]
1846#[serde(rename_all = "camelCase")]
1847pub struct InvoiceLine {
1848 pub workspace: String,
1849 pub amount_micros: i64,
1850}
1851
1852/// A workspace's invoice from g1t: one per month, and one each time it is
1853/// charged near its limit. Itemised, charged to the card on file, and kept
1854/// in Stripe's billing page with its PDF.
1855#[derive(Clone, Debug, Serialize, Deserialize)]
1856#[serde(rename_all = "camelCase")]
1857pub struct WorkspaceInvoice {
1858 pub invoice_id: String,
1859 pub workspace: String,
1860 /// `month` (2026-10) or `threshold`.
1861 pub reason: String,
1862 pub period: String,
1863 pub amount_micros: i64,
1864 /// `paid`, `open`, `failed` or `void`.
1865 pub status: String,
1866 pub hosted_url: Option<String>,
1867 pub pdf_url: Option<String>,
1868 pub lines: Vec<InvoiceItem>,
1869 pub created_at: String,
1870 /// The card processing fee on top of `amount_micros`, on invoices from
1871 /// before g1t's prices included it; 0 on every invoice since.
1872 #[serde(default)]
1873 pub fee_micros: i64,
1874 /// The tax Stripe added on top, once it is known (after paying).
1875 #[serde(default)]
1876 pub tax_micros: i64,
1877}
1878
1879#[derive(Clone, Debug, Serialize, Deserialize)]
1880#[serde(rename_all = "camelCase")]
1881pub struct InvoiceItem {
1882 pub description: String,
1883 pub amount_micros: i64,
1884}
1885
1886/// `invoices`: a workspace's invoices from g1t, newest first. Owners and
1887/// billing managers only. Returns `Outcome<Vec<WorkspaceInvoice>>`.
1888#[derive(Debug, Serialize, Deserialize)]
1889pub struct InvoicesArgs {
1890 pub workspace: String,
1891 pub viewer: Viewer,
1892}
1893
1894/// `admin_workspace_invoices`: the same, for staff. Returns
1895/// `Vec<WorkspaceInvoice>`.
1896#[derive(Debug, Serialize, Deserialize)]
1897pub struct AdminWorkspaceInvoicesArgs {
1898 pub workspace: String,
1899}
1900
1901/// `statement`: a month of a workspace's ledger, grouped by day (or by
1902/// project) with a line per kind of charge. Members only. Returns
1903/// `Outcome<Statement>`.
1904#[derive(Debug, Serialize, Deserialize)]
1905pub struct StatementArgs {
1906 pub workspace: String,
1907 pub viewer: Viewer,
1908 /// YYYY-MM; this month when absent.
1909 #[serde(default)]
1910 pub month: Option<String>,
1911 /// `day` (the default) or `project`.
1912 #[serde(default)]
1913 pub group: Option<String>,
1914}
1915
1916#[derive(Clone, Debug, Serialize, Deserialize)]
1917#[serde(rename_all = "camelCase")]
1918pub struct Statement {
1919 pub month: String,
1920 /// Months with any entries, newest first.
1921 pub months: Vec<String>,
1922 pub groups: Vec<StatementGroup>,
1923 pub totals: StatementTotals,
1924}
1925
1926#[derive(Clone, Debug, Serialize, Deserialize)]
1927#[serde(rename_all = "camelCase")]
1928pub struct StatementGroup {
1929 /// The day (YYYY-MM-DD) or the project (`owner/name`, or empty).
1930 pub key: String,
1931 pub label: String,
1932 pub lines: Vec<StatementLine>,
1933 /// What the group's charges come to.
1934 pub charged_micros: i64,
1935 /// Its usage at price, and what the discount took off it.
1936 #[serde(default)]
1937 pub price_micros: i64,
1938 #[serde(default)]
1939 pub discount_micros: i64,
1940}
1941
1942#[derive(Clone, Debug, Serialize, Deserialize)]
1943#[serde(rename_all = "camelCase")]
1944pub struct StatementLine {
1945 /// Agent runs, Sandbox time, Deployments, Payments, Credits from g1t,
1946 /// Refunds, and, for older entries, Runs on your own model provider.
1947 pub kind: String,
1948 pub count: u32,
1949 /// Charges positive; money in (payments, credits) negative.
1950 pub charged_micros: i64,
1951 pub cost_micros: i64,
1952 /// Of the usage on the line, what was paid for before it was charged:
1953 /// by the plan's included usage, the trial credit, g1t's open-source
1954 /// pool, or g1t itself. Not in `charged_micros`.
1955 #[serde(default)]
1956 pub covered_micros: i64,
1957 /// Usage at its price: charged, plus what paid for it and what the
1958 /// discount took off. Zero for money in.
1959 #[serde(default)]
1960 pub price_micros: i64,
1961 /// What the account's discount took off the line's price.
1962 #[serde(default)]
1963 pub discount_micros: i64,
1964 /// On the `Tax` and `Card processing fees` lines: what was paid with
1965 /// payments on top of what reached the balance (negative for what a
1966 /// refund gave back). Never in `charged_micros` or the balance.
1967 #[serde(default)]
1968 pub passed_micros: i64,
1969 /// On a `Runner minutes, <machine>` line: the minutes charged, each
1970 /// job's time rounded up to the minute.
1971 #[serde(default, skip_serializing_if = "Option::is_none")]
1972 pub minutes: Option<i64>,
1973}
1974
1975#[derive(Clone, Debug, Serialize, Deserialize)]
1976#[serde(rename_all = "camelCase")]
1977pub struct StatementTotals {
1978 pub charged_micros: i64,
1979 pub paid_micros: i64,
1980 pub cost_micros: i64,
1981 pub entries: u32,
1982 /// Usage at price, and what the discount took off it: charged is the
1983 /// price less the discount and what paid for it.
1984 #[serde(default)]
1985 pub price_micros: i64,
1986 #[serde(default)]
1987 pub discount_micros: i64,
1988 /// The account's discount now, in percent; absent without one.
1989 #[serde(default)]
1990 pub discount_percent: Option<u32>,
1991 /// What paid for usage before it was charged, one line per source,
1992 /// such as "Paid by g1t's open-source pool".
1993 #[serde(default)]
1994 pub covered: Vec<Covered>,
1995 /// Owed when the month closed but under the minimum charge, so it
1996 /// carries over to the next invoice. Zero when nothing carried.
1997 #[serde(default)]
1998 pub carried_micros: i64,
1999 /// Tax paid with the month's payments, on top of `paid_micros`, and
2000 /// card processing fees paid with payments from before g1t's prices
2001 /// included them.
2002 #[serde(default)]
2003 pub tax_micros: i64,
2004 #[serde(default)]
2005 pub card_fee_micros: i64,
2006}
2007
2008/// One source that paid for usage before it was charged.
2009#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
2010#[serde(rename_all = "camelCase")]
2011pub struct Covered {
2012 /// `included`, `trial`, `oss_pool` or `given`.
2013 pub source: String,
2014 /// "Paid by your plan's included usage", "Paid by your trial credit",
2015 /// "Paid by g1t's open-source pool", "Covered by g1t".
2016 pub label: String,
2017 pub micros: i64,
2018}
2019
2020/// `statement_entries`: one statement line's entries, newest first, 50 at
2021/// a time (`before` = the last id seen). Returns `Outcome<Vec<LedgerEntry>>`.
2022#[derive(Debug, Serialize, Deserialize)]
2023pub struct StatementEntriesArgs {
2024 pub workspace: String,
2025 pub viewer: Viewer,
2026 pub month: String,
2027 pub kind: String,
2028 #[serde(default)]
2029 pub day: Option<String>,
2030 #[serde(default)]
2031 pub project: Option<String>,
2032 #[serde(default)]
2033 pub before: Option<String>,
2034}
2035
2036// --- Sales (sudo.g1t.sh) ------------------------------------------------------
2037//
2038// What staff need to know to reach out: who is growing, who is close to
2039// their limit, who was declined, who has become a steady customer. And what
2040// was done about it: a stage, an owner on g1t's side, a next step, notes.
2041
2042/// Why a workspace is worth a look.
2043#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize)]
2044#[serde(rename_all = "snake_case")]
2045pub enum SignalKind {
2046 /// At its limit, or its own spend limit: work is stopped.
2047 AtLimit,
2048 /// Past 80% of what is available to it: about to need more.
2049 NearCeiling,
2050 /// Its card was declined or a payment disputed.
2051 Declined,
2052 /// This month is well ahead of last month.
2053 Growing,
2054 /// Became Established: the ceiling now follows its spend.
2055 Established,
2056 /// Paid g1t for the first time.
2057 FirstPayment,
2058 /// Spending enough that custom terms or an enterprise may suit it.
2059 HighSpend,
2060 /// Costs g1t more on Cloudflare than it pays, over 30 days: a pricing
2061 /// gap or abuse to look at (billing's `margin`).
2062 CostOverRevenue,
2063}
2064
2065#[derive(Clone, Debug, Serialize, Deserialize)]
2066#[serde(rename_all = "camelCase")]
2067pub struct Signal {
2068 pub workspace: String,
2069 pub kind: SignalKind,
2070 /// One sentence, with the figures.
2071 pub detail: String,
2072 /// The figure that matters, such as this month's spend.
2073 pub value_micros: i64,
2074 /// Its sales stage, if staff gave it one.
2075 pub stage: Option<String>,
2076 pub owner: Option<String>,
2077 #[serde(default)]
2078 pub next_step: Option<String>,
2079 /// When the next step is due, `YYYY-MM-DD`.
2080 #[serde(default)]
2081 pub next_at: Option<String>,
2082}
2083
2084/// `admin_invoices`: every invoice g1t has sent, workspaces' and
2085/// enterprises', newest first. Returns `Vec<InvoiceSummary>`.
2086#[derive(Debug, Default, Serialize, Deserialize)]
2087pub struct AdminInvoicesArgs {
2088 /// `paid`, `open`, `failed`, `overdue` or `void`.
2089 #[serde(default)]
2090 pub status: Option<String>,
2091 /// YYYY-MM, by when it was sent.
2092 #[serde(default)]
2093 pub month: Option<String>,
2094}
2095
2096#[derive(Clone, Debug, Serialize, Deserialize)]
2097#[serde(rename_all = "camelCase")]
2098pub struct InvoiceSummary {
2099 pub invoice_id: String,
2100 /// `workspace` or `enterprise`.
2101 pub kind: String,
2102 /// The workspace's slug, or the enterprise's account id.
2103 pub account: String,
2104 /// What to call it: the workspace, or the enterprise's name.
2105 pub name: String,
2106 pub reason: String,
2107 pub period: String,
2108 pub amount_micros: i64,
2109 pub status: String,
2110 pub hosted_url: Option<String>,
2111 pub created_at: String,
2112 pub paid_at: Option<String>,
2113}
2114
2115/// `admin_audit`: every change made in sudo, and by Stripe, newest first.
2116/// Returns `Vec<AdminAction>`.
2117#[derive(Debug, Default, Serialize, Deserialize)]
2118pub struct AdminAuditArgs {
2119 #[serde(default)]
2120 pub by: Option<String>,
2121 #[serde(default)]
2122 pub action: Option<String>,
2123 /// Only those before this time, for paging.
2124 #[serde(default)]
2125 pub before: Option<String>,
2126}
2127
2128/// `admin_signals`: every workspace worth reaching out to, most urgent
2129/// first. Returns `Vec<Signal>`.
2130#[derive(Debug, Default, Serialize, Deserialize)]
2131pub struct AdminSignalsArgs {}
2132
2133/// What staff are doing about a workspace.
2134#[derive(Clone, Debug, Serialize, Deserialize)]
2135#[serde(rename_all = "camelCase")]
2136pub struct SalesRecord {
2137 pub workspace: String,
2138 /// `none`, `lead`, `contacted`, `negotiating`, `won`, `lost` or `churn_risk`.
2139 pub stage: String,
2140 /// The staff member looking after it.
2141 pub owner: Option<String>,
2142 pub next_step: Option<String>,
2143 /// RFC 3339 date.
2144 pub next_at: Option<String>,
2145 pub notes: Vec<SalesNote>,
2146 pub updated_at: Option<String>,
2147}
2148
2149#[derive(Clone, Debug, Serialize, Deserialize)]
2150#[serde(rename_all = "camelCase")]
2151pub struct SalesNote {
2152 pub id: String,
2153 pub text: String,
2154 pub by: String,
2155 pub created_at: String,
2156}
2157
2158/// `admin_sales`: a workspace's sales record. Returns `SalesRecord`.
2159#[derive(Debug, Serialize, Deserialize)]
2160pub struct AdminSalesArgs {
2161 pub workspace: String,
2162}
2163
2164/// `admin_set_sales`: its stage, owner and next step. Returns `Outcome<SalesRecord>`.
2165#[derive(Debug, Serialize, Deserialize)]
2166pub struct AdminSetSalesArgs {
2167 pub workspace: String,
2168 pub stage: String,
2169 #[serde(default)]
2170 pub owner: Option<String>,
2171 #[serde(default)]
2172 pub next_step: Option<String>,
2173 #[serde(default)]
2174 pub next_at: Option<String>,
2175 pub by: String,
2176}
2177
2178/// `admin_add_note`. Returns `Outcome<SalesRecord>`.
2179#[derive(Debug, Serialize, Deserialize)]
2180pub struct AdminAddNoteArgs {
2181 pub workspace: String,
2182 pub text: String,
2183 pub by: String,
2184}
2185
2186/// `admin_overview`: the business at a glance. Returns `Overview`.
2187#[derive(Debug, Default, Serialize, Deserialize)]
2188pub struct AdminOverviewArgs {}
2189
2190#[derive(Clone, Debug, Serialize, Deserialize)]
2191#[serde(rename_all = "camelCase")]
2192pub struct Overview {
2193 /// YYYY-MM.
2194 pub month: String,
2195 /// The last six months, oldest first, all workspaces together.
2196 pub months: Vec<MonthFigures>,
2197 /// This month by kind of usage: models, sandbox, deployments, plans.
2198 pub by_kind: Vec<KindFigures>,
2199 pub paying_workspaces: u32,
2200 pub stopped: u32,
2201 pub near_ceiling: u32,
2202 pub declined: u32,
2203 /// Sent and not yet paid, workspaces and enterprises.
2204 pub open_invoices_micros: i64,
2205 /// Follow-ups due today or earlier.
2206 pub follow_ups_due: u32,
2207 /// The capped budgets g1t pays from, this month.
2208 #[serde(default)]
2209 pub pools: Option<Pools>,
2210 /// This month's revenue: usage charged plus the plan's price paid.
2211 #[serde(default)]
2212 pub revenue_micros: i64,
2213 /// Workspaces on the paid plan now, and what their price comes to a
2214 /// month.
2215 #[serde(default)]
2216 pub active_plans: u32,
2217 #[serde(default)]
2218 pub plan_mrr_micros: i64,
2219 /// What g1t gave this month, by source, apart from its margin.
2220 #[serde(default)]
2221 pub given: Vec<GivenFigures>,
2222 /// This month so far by the Costs & margin model (`margin_days`), the
2223 /// same figures the Costs page adds up: money in, every cost
2224 /// (Cloudflare's bill included), and what was given by why, credits
2225 /// spent among it. Through `margin_through`, the last day rolled up.
2226 /// None before the first day of the month is rolled up.
2227 #[serde(default)]
2228 pub month_margin: Option<OverallMargin>,
2229 /// The last day `month_margin` holds. YYYY-MM-DD.
2230 #[serde(default)]
2231 pub margin_through: Option<String>,
2232 /// g1t's own and Flagon's workspaces this month: what their use cost,
2233 /// and why they are not charged.
2234 #[serde(default)]
2235 pub internal: Vec<InternalUse>,
2236 /// Open limit requests, and workspaces in the Overages queue.
2237 #[serde(default)]
2238 pub open_requests: u32,
2239 #[serde(default)]
2240 pub overages: u32,
2241 /// Spend spikes waiting for an owner.
2242 #[serde(default)]
2243 pub open_spikes: u32,
2244}
2245
2246/// What g1t gave this month from one source.
2247#[derive(Clone, Debug, Default, PartialEq, Serialize, Deserialize)]
2248#[serde(rename_all = "camelCase")]
2249pub struct GivenFigures {
2250 /// `internal`, `trial`, `oss_pool`, `goodwill` or `covered`.
2251 pub source: String,
2252 pub label: String,
2253 /// At price, and what it cost g1t.
2254 pub micros: i64,
2255 pub cost_micros: i64,
2256}
2257
2258/// One internal workspace's use this month.
2259#[derive(Clone, Debug, Default, PartialEq, Serialize, Deserialize)]
2260#[serde(rename_all = "camelCase")]
2261pub struct InternalUse {
2262 pub workspace: String,
2263 /// Why it is not charged: its terms' note.
2264 pub reason: String,
2265 pub cost_micros: i64,
2266 pub entries: u32,
2267}
2268
2269/// g1t's capped budgets for free usage, this calendar month (UTC).
2270#[derive(Clone, Debug, Default, Serialize, Deserialize)]
2271#[serde(rename_all = "camelCase")]
2272pub struct Pools {
2273 /// YYYY-MM.
2274 pub month: String,
2275 /// Trial grants made this month, against the month's pool.
2276 pub trial_granted_micros: i64,
2277 pub trial_pool_micros: i64,
2278 pub trial_grants: u32,
2279 /// What the open-source pool paid this month, against its cap.
2280 pub oss_used_micros: i64,
2281 pub oss_pool_micros: i64,
2282 /// Each public repository's monthly cap on the pool.
2283 pub oss_repo_micros: i64,
2284}
2285
2286#[derive(Clone, Debug, Serialize, Deserialize)]
2287#[serde(rename_all = "camelCase")]
2288pub struct KindFigures {
2289 pub kind: String,
2290 pub charged_micros: i64,
2291 pub cost_micros: i64,
2292}
2293
2294// --- Staff (sudo.g1t.sh) ------------------------------------------------------
2295//
2296// Called only by the sudo app, which only g1t staff can reach (behind
2297// Cloudflare Access). Each change names who made it, and is kept in the
2298// audit log.
2299
2300/// `admin_accounts`: every billing account, with where each stands this
2301/// month. Returns `Vec<AccountSummary>`.
2302#[derive(Debug, Default, Serialize, Deserialize)]
2303pub struct AdminAccountsArgs {
2304 #[serde(default)]
2305 pub query: Option<String>,
2306 /// Exactly these workspaces' accounts, such as one page of sudo's
2307 /// list; every account with activity when absent.
2308 #[serde(default)]
2309 pub workspaces: Option<Vec<String>>,
2310}
2311
2312#[derive(Clone, Debug, Serialize, Deserialize)]
2313#[serde(rename_all = "camelCase")]
2314pub struct AccountSummary {
2315 pub account: BillingAccount,
2316 pub limit: Limit,
2317 /// Charged this month, after terms.
2318 pub charged_micros: i64,
2319 /// What this month's usage cost g1t.
2320 pub cost_micros: i64,
2321 /// Paid, ever.
2322 pub paid_micros: i64,
2323 /// The same figures for each of the account's workspaces that has
2324 /// any, so staff can see what one member of an enterprise used.
2325 #[serde(default)]
2326 pub by_workspace: Vec<WorkspaceFigures>,
2327 /// The last six months, oldest first, for trends.
2328 #[serde(default)]
2329 pub months: Vec<MonthFigures>,
2330}
2331
2332/// One month of an account's billing.
2333#[derive(Clone, Debug, Default, PartialEq, Serialize, Deserialize)]
2334#[serde(rename_all = "camelCase")]
2335pub struct MonthFigures {
2336 /// YYYY-MM.
2337 pub month: String,
2338 /// Usage charged, after what paid for it first.
2339 pub charged_micros: i64,
2340 /// What usage cost g1t: only what g1t paid for, never a workspace's own
2341 /// model provider.
2342 pub cost_micros: i64,
2343 pub paid_micros: i64,
2344 /// The plan's monthly price, paid.
2345 #[serde(default)]
2346 pub plans_micros: i64,
2347 /// What g1t gave, at price: internal (comped) use, trials, the
2348 /// open-source pool, goodwill credits and what g1t covered. Not margin.
2349 #[serde(default)]
2350 pub given_micros: i64,
2351}
2352
2353/// One workspace's share of an [`AccountSummary`].
2354#[derive(Clone, Debug, Default, PartialEq, Serialize, Deserialize)]
2355#[serde(rename_all = "camelCase")]
2356pub struct WorkspaceFigures {
2357 pub workspace: String,
2358 pub charged_micros: i64,
2359 pub cost_micros: i64,
2360 pub paid_micros: i64,
2361}
2362
2363/// `admin_account`: one account in full. Returns `Outcome<AccountDetail>`.
2364#[derive(Debug, Serialize, Deserialize)]
2365pub struct AdminAccountArgs {
2366 /// An account id, or a workspace slug.
2367 pub id: String,
2368}
2369
2370#[derive(Clone, Debug, Serialize, Deserialize)]
2371#[serde(rename_all = "camelCase")]
2372pub struct AccountDetail {
2373 pub summary: AccountSummary,
2374 /// Each workspace's limit, for an enterprise.
2375 pub workspaces: Vec<Limit>,
2376 pub ledger: Vec<LedgerEntry>,
2377 pub audit: Vec<AdminAction>,
2378}
2379
2380/// `admin_set_terms`. Returns `Outcome<BillingAccount>`.
2381#[derive(Debug, Serialize, Deserialize)]
2382pub struct AdminSetTermsArgs {
2383 pub id: String,
2384 pub terms: Terms,
2385 pub by: String,
2386}
2387
2388/// `admin_create_enterprise`. Returns `Outcome<BillingAccount>`.
2389#[derive(Debug, Serialize, Deserialize)]
2390pub struct AdminCreateEnterpriseArgs {
2391 pub name: String,
2392 pub workspaces: Vec<String>,
2393 pub by: String,
2394}
2395
2396/// `admin_attach`: moves a workspace onto an enterprise account, or back
2397/// onto its own with `account: None`. Returns `Outcome<BillingAccount>`.
2398#[derive(Debug, Serialize, Deserialize)]
2399pub struct AdminAttachArgs {
2400 pub workspace: String,
2401 pub account: Option<String>,
2402 pub by: String,
2403}
2404
2405/// Why g1t gave a workspace credit.
2406#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize, Deserialize)]
2407#[serde(rename_all = "snake_case")]
2408pub enum CreditKind {
2409 /// Marketing: a welcome, a referral, an event. Given away when spent.
2410 Promotional,
2411 /// An apology, or accidental usage forgiven. Given away when spent.
2412 #[default]
2413 Goodwill,
2414 /// Money back for something that went wrong. Not given away: it gives
2415 /// back money already paid, so it comes off what was paid on the day
2416 /// it refunds, and what it pays for later is paid for.
2417 Refund,
2418 /// Bought by the workspace (prepaid AI): money paid in up front, owed
2419 /// as usage until spent. What it pays for is paid for, never given.
2420 /// Staff never give it; its ledger line is a payment, not `crd…`.
2421 Purchased,
2422}
2423
2424impl CreditKind {
2425 pub fn as_str(self) -> &'static str {
2426 match self {
2427 CreditKind::Promotional => "promotional",
2428 CreditKind::Goodwill => "goodwill",
2429 CreditKind::Refund => "refund",
2430 CreditKind::Purchased => "purchased",
2431 }
2432 }
2433
2434 pub fn parse(text: &str) -> Option<CreditKind> {
2435 match text {
2436 "promotional" => Some(CreditKind::Promotional),
2437 "goodwill" => Some(CreditKind::Goodwill),
2438 "refund" => Some(CreditKind::Refund),
2439 "purchased" => Some(CreditKind::Purchased),
2440 _ => None,
2441 }
2442 }
2443
2444 /// As people read it: `Promotional`.
2445 pub fn label(self) -> &'static str {
2446 match self {
2447 CreditKind::Promotional => "Promotional",
2448 CreditKind::Goodwill => "Goodwill",
2449 CreditKind::Refund => "Refund",
2450 CreditKind::Purchased => "Purchased",
2451 }
2452 }
2453}
2454
2455/// `admin_credit`: credit g1t gives a workspace: promotional, goodwill or a
2456/// refund, with a note, and optionally an expiry. It is spent before
2457/// anything paid in advance, the soonest-expiring first. The workspace's
2458/// owners are emailed. Returns `Outcome<LedgerEntry>`.
2459#[derive(Debug, Serialize, Deserialize)]
2460pub struct AdminCreditArgs {
2461 pub workspace: String,
2462 pub amount_micros: i64,
2463 pub note: String,
2464 pub by: String,
2465 #[serde(default)]
2466 pub kind: CreditKind,
2467 /// RFC 3339; unused credit stops counting then. Never for a refund.
2468 #[serde(default)]
2469 pub expires_at: Option<String>,
2470 /// A refund: what it refunds, in a line, and the day of it
2471 /// (`YYYY-MM-DD`; today if absent).
2472 #[serde(default)]
2473 pub refund_for: Option<String>,
2474 #[serde(default)]
2475 pub refund_day: Option<String>,
2476}
2477
2478/// One credit g1t gave, with what of it was used: spent on usage, the
2479/// soonest-expiring grant first, before anything paid in advance.
2480#[derive(Clone, Debug, Default, PartialEq, Serialize, Deserialize)]
2481#[serde(rename_all = "camelCase")]
2482pub struct CreditGrant {
2483 /// `crd_…`, the grant's ledger reference.
2484 pub id: String,
2485 pub workspace: String,
2486 pub kind: CreditKind,
2487 pub amount_micros: i64,
2488 pub used_micros: i64,
2489 /// What can still be spent: nothing once it expired or was revoked.
2490 pub left_micros: i64,
2491 pub note: String,
2492 #[serde(default)]
2493 pub refund_for: Option<String>,
2494 #[serde(default)]
2495 pub refund_day: Option<String>,
2496 pub expires_at: Option<String>,
2497 pub created_by: String,
2498 pub created_at: String,
2499 /// `open`, `used`, `expired` or `revoked`.
2500 pub state: String,
2501 #[serde(default)]
2502 pub closed_at: Option<String>,
2503 #[serde(default)]
2504 pub closed_note: Option<String>,
2505 #[serde(default)]
2506 pub closed_by: Option<String>,
2507 /// What expiring or revoking took off the balance.
2508 #[serde(default)]
2509 pub closed_micros: i64,
2510 /// What it pays for: `all` usage, or `models` only (agent runs' model
2511 /// cost), which is spent first.
2512 #[serde(default)]
2513 pub scope: String,
2514 /// Where it came from: `staff`, `purchase` or `promo_code`.
2515 #[serde(default)]
2516 pub source: String,
2517}
2518
2519/// `credits` (`Outcome<Credits>`, `AccountArgs`): a workspace's credits from
2520/// g1t, newest first, for its members.
2521#[derive(Clone, Debug, Default, Serialize, Deserialize)]
2522#[serde(rename_all = "camelCase")]
2523pub struct Credits {
2524 pub grants: Vec<CreditGrant>,
2525 /// What is left to spend, in all.
2526 pub left_micros: i64,
2527}
2528
2529/// `admin_credits`: every credit g1t gave, newest first, filtered. Returns
2530/// `AdminCredits`.
2531#[derive(Debug, Default, Serialize, Deserialize)]
2532pub struct AdminCreditsArgs {
2533 #[serde(default)]
2534 pub workspace: Option<String>,
2535 #[serde(default)]
2536 pub kind: Option<CreditKind>,
2537 /// `YYYY-MM`: given that month.
2538 #[serde(default)]
2539 pub month: Option<String>,
2540 /// Given by this member of staff.
2541 #[serde(default)]
2542 pub by: Option<String>,
2543}
2544
2545/// One month's credits of one kind: given, used on usage that month, and
2546/// taken back unused (expired or revoked).
2547#[derive(Clone, Debug, Default, PartialEq, Serialize, Deserialize)]
2548#[serde(rename_all = "camelCase")]
2549pub struct CreditMonth {
2550 pub month: String,
2551 pub kind: CreditKind,
2552 pub given_micros: i64,
2553 pub grants: u32,
2554 pub used_micros: i64,
2555 pub expired_micros: i64,
2556 pub revoked_micros: i64,
2557}
2558
2559#[derive(Clone, Debug, Default, Serialize, Deserialize)]
2560#[serde(rename_all = "camelCase")]
2561pub struct AdminCredits {
2562 /// At most 200.
2563 pub grants: Vec<CreditGrant>,
2564 /// The last 12 months, newest first, whatever the month filter.
2565 pub months: Vec<CreditMonth>,
2566 /// Who has given credit, for the filter.
2567 pub staff: Vec<String>,
2568}
2569
2570/// `admin_revoke_credit`: what is left of a grant, taken off the balance,
2571/// with why. Returns `Outcome<CreditGrant>`.
2572#[derive(Debug, Serialize, Deserialize)]
2573pub struct AdminRevokeCreditArgs {
2574 pub id: String,
2575 pub note: String,
2576 pub by: String,
2577}
2578
2579/// `admin_reset_billing`: a test workspace's billing wiped, so it starts
2580/// again as a new customer. Only while billing runs on Stripe's test key;
2581/// never a comped workspace or one an enterprise pays for. `confirm` is the
2582/// workspace's slug typed out. Returns `Outcome<BillingReset>`.
2583#[derive(Debug, Serialize, Deserialize)]
2584pub struct AdminResetBillingArgs {
2585 pub workspace: String,
2586 pub confirm: String,
2587 pub note: String,
2588 pub by: String,
2589}
2590
2591/// What a reset removed.
2592#[derive(Clone, Debug, Serialize, Deserialize)]
2593#[serde(rename_all = "camelCase")]
2594pub struct BillingReset {
2595 pub workspace: String,
2596 pub rows: u32,
2597 /// Whether the costs analysis ran again after it, so the margin
2598 /// figures no longer hold the workspace's past usage.
2599 #[serde(default)]
2600 pub refreshed: bool,
2601}
2602
2603/// `admin_detach_test_stripe`: workspaces moved off what Stripe's test mode
2604/// left behind, before billing takes a live key, without wiping their
2605/// history (that is `admin_reset_billing`). Plans canceled, cached cards and
2606/// customers forgotten, open invoices void, past months marked closed, test
2607/// payments reversed and what was owed written off as goodwill. Refused on a
2608/// live key. Snake_case both ways. Returns `Outcome<TestStripeDetach>`.
2609#[derive(Clone, Debug, Default, Serialize, Deserialize)]
2610pub struct AdminDetachTestStripeArgs {
2611 /// One workspace (any but an enterprise's, comped ones included), or
2612 /// none for every workspace not comped, not an enterprise's and not in
2613 /// `exclude`.
2614 #[serde(default)]
2615 pub workspace: Option<String>,
2616 /// For every workspace: slugs left alone. None: `syntaqx` and `flagon-io`.
2617 #[serde(default)]
2618 pub exclude: Option<Vec<String>>,
2619 /// What would change, changing nothing.
2620 #[serde(default)]
2621 pub dry_run: bool,
2622 /// Also cancel each plan's subscription at Stripe: only on a test key.
2623 #[serde(default)]
2624 pub cancel_at_stripe: bool,
2625 /// The workspace's slug, or `detach` for every workspace. Not needed
2626 /// for a dry run.
2627 #[serde(default)]
2628 pub confirm: String,
2629 pub by: String,
2630}
2631
2632/// What a detach changed, or would change, for each workspace.
2633#[derive(Clone, Debug, Default, Serialize, Deserialize, PartialEq)]
2634pub struct TestStripeDetach {
2635 pub dry_run: bool,
2636 /// Workspaces with something to change, by slug.
2637 pub workspaces: Vec<WorkspaceDetach>,
2638 /// Workspaces holding billing rows that were left alone, and why.
2639 pub skipped: Vec<DetachSkipped>,
2640 /// Workspaces with something to change that this call did not reach
2641 /// (a call detaches at most 25): run it again.
2642 pub remaining: u32,
2643}
2644
2645#[derive(Clone, Debug, Default, Serialize, Deserialize, PartialEq)]
2646pub struct DetachSkipped {
2647 pub workspace: String,
2648 pub reason: String,
2649}
2650
2651/// One workspace's detach.
2652#[derive(Clone, Debug, Default, Serialize, Deserialize, PartialEq)]
2653pub struct WorkspaceDetach {
2654 pub workspace: String,
2655 /// Whether it was done (false on a dry run).
2656 pub done: bool,
2657 /// A Stripe customer id, cleared.
2658 pub customer: bool,
2659 /// The cached card, such as `visa 4242`, cleared.
2660 pub card: Option<String>,
2661 /// Cached card or tax-address fields cleared.
2662 pub card_cached: bool,
2663 /// Plans set to canceled.
2664 pub subscriptions: u32,
2665 /// Card checks removed (the trial grant stays: no second trial).
2666 pub card_checks: u32,
2667 /// Open checkouts removed.
2668 pub checkouts: u32,
2669 /// AI credit auto-reload turned off.
2670 pub ai_reload: bool,
2671 /// A declined autopay cleared.
2672 pub autopay_cleared: bool,
2673 /// Open or failed invoices set to void.
2674 pub invoices_voided: u32,
2675 /// Past months marked closed as skipped, so the first live close does
2676 /// not charge them.
2677 pub months_skipped: Vec<String>,
2678 /// AI or purchased credit closed, and what was left of it.
2679 pub credit_grants_closed: u32,
2680 pub credit_closed_micros: i64,
2681 /// Test-mode payments reversed on the ledger.
2682 pub payments_reversed_micros: i64,
2683 /// Test-mode plan payments reversed.
2684 pub plan_payments_reversed_micros: i64,
2685 /// What was owed, written off as goodwill credit.
2686 pub written_off_micros: i64,
2687 pub balance_before_micros: i64,
2688 pub balance_after_micros: i64,
2689 /// Subscriptions canceled at Stripe, and what Stripe refused.
2690 pub stripe_canceled: u32,
2691 pub stripe_errors: Vec<String>,
2692 /// Metered figures of months before this one marked charged without a
2693 /// charge, and what they would have been charged.
2694 #[serde(default)]
2695 pub pending_months_marked: u32,
2696 #[serde(default)]
2697 pub pending_marked_micros: i64,
2698 /// This month's metered figures kept as the baseline later reports are
2699 /// charged past, and what they come to.
2700 #[serde(default)]
2701 pub pending_baselined: u32,
2702 #[serde(default)]
2703 pub pending_baselined_micros: i64,
2704 /// This month's draws on the plan's included usage removed, so a live
2705 /// plan this month has all of it.
2706 #[serde(default)]
2707 pub plan_credit_cleared_micros: i64,
2708 /// What lost test-mode disputes left owed, cleared.
2709 #[serde(default)]
2710 pub dispute_owed_cleared_micros: i64,
2711 /// Open test-mode disputes closed.
2712 #[serde(default)]
2713 pub disputes_closed: u32,
2714}
2715
2716/// One change made in sudo.
2717#[derive(Clone, Debug, Serialize, Deserialize)]
2718#[serde(rename_all = "camelCase")]
2719pub struct AdminAction {
2720 pub id: String,
2721 pub account: String,
2722 pub action: String,
2723 pub detail: String,
2724 pub by: String,
2725 pub created_at: String,
2726}
2727
2728/// What a feature's plan costs and includes.
2729#[derive(Clone, Debug, Serialize, Deserialize)]
2730#[serde(rename_all = "camelCase")]
2731pub struct Plan {
2732 pub feature: Feature,
2733 pub title: String,
2734 /// Charged every month while the plan is on, in cents, excluding tax:
2735 /// all of it, with no card fee on top.
2736 pub monthly_cents: u32,
2737 /// What the monthly price includes, one line each, for people to read.
2738 pub includes: Vec<String>,
2739 /// How usage past the allowance is charged, for people to read.
2740 pub overage: String,
2741}
2742
2743#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize)]
2744#[serde(rename_all = "snake_case")]
2745pub enum SubscriptionStatus {
2746 /// Paid up; the feature works.
2747 Active,
2748 /// Paid up to the end of the period, and ends then.
2749 Canceling,
2750 /// The last payment failed; the feature is off until it is paid.
2751 PastDue,
2752 /// Ended.
2753 Canceled,
2754}
2755
2756impl SubscriptionStatus {
2757 /// Whether the feature works in this state.
2758 pub fn on(self) -> bool {
2759 matches!(self, SubscriptionStatus::Active | SubscriptionStatus::Canceling)
2760 }
2761}
2762
2763/// A workspace's plan for one feature.
2764#[derive(Clone, Debug, Serialize, Deserialize)]
2765#[serde(rename_all = "camelCase")]
2766pub struct Subscription {
2767 pub feature: Feature,
2768 pub status: SubscriptionStatus,
2769 /// RFC 3339: when the period paid for ends, and the plan renews or
2770 /// ends.
2771 pub period_end: Option<String>,
2772 /// Username of whoever turned it on.
2773 pub started_by: String,
2774 /// RFC 3339.
2775 pub started_at: String,
2776}
2777
2778/// A feature as a workspace sees it: what it costs, and its plan if it has
2779/// one.
2780#[derive(Clone, Debug, Serialize, Deserialize)]
2781#[serde(rename_all = "camelCase")]
2782pub struct FeatureState {
2783 pub plan: Plan,
2784 pub subscription: Option<Subscription>,
2785 /// Whether the feature works for the workspace now.
2786 pub on: bool,
2787 /// On without a plan: comped terms, or given by g1t. Nothing to pay
2788 /// and nothing to turn off.
2789 #[serde(default)]
2790 pub included: bool,
2791}
2792
2793/// `features`: every paid feature and the workspace's plan for each.
2794/// Members only. Returns `Outcome<Vec<FeatureState>>`.
2795#[derive(Debug, Serialize, Deserialize)]
2796pub struct FeaturesArgs {
2797 pub workspace: String,
2798 pub viewer: Viewer,
2799}
2800
2801/// `subscribe`: starts the card page for a feature's monthly plan. Owners
2802/// only. Returns `Outcome<Checkout>`; the page's id comes back to
2803/// `return_url` as `session`, for `confirm_subscription`.
2804#[derive(Debug, Serialize, Deserialize)]
2805#[serde(rename_all = "camelCase")]
2806pub struct SubscribeArgs {
2807 pub actor: User,
2808 pub workspace: String,
2809 pub feature: Feature,
2810 pub return_url: String,
2811}
2812
2813/// `confirm_subscription`: turns the feature on once the processor says
2814/// the plan was paid for. Safe to call any number of times. Returns
2815/// `Outcome<FeatureState>`.
2816#[derive(Debug, Serialize, Deserialize)]
2817pub struct ConfirmSubscriptionArgs {
2818 pub workspace: String,
2819 pub viewer: Viewer,
2820 pub session: String,
2821}
2822
2823/// `admin_log`: a staff change another service made to a workspace, kept
2824/// in sudo's audit log with billing's own (`admin_audit`). For identity's
2825/// restores and purges of deleted workspaces. Returns `bool`.
2826#[derive(Debug, Serialize, Deserialize)]
2827pub struct AdminLogArgs {
2828 pub workspace: String,
2829 pub action: String,
2830 pub detail: String,
2831 /// The staff member's email.
2832 pub by: String,
2833}
2834
2835/// `close_workspace`: settles a workspace that is about to be deleted.
2836/// Owners only. Refused while it has an invoice that failed, while it
2837/// holds prepaid credit, or while it owes money it cannot be charged for
2838/// now; otherwise what it owes is invoiced to its card at once (no
2839/// minimum), its plan is cancelled at Stripe straight away, and its
2840/// account is marked closed, so the month-end close, autopay and limit
2841/// warnings pass it by. Its ledger, invoices and statements stay. With
2842/// `dry_run`, only says whether it could, changing nothing. Returns
2843/// `Outcome<bool>`.
2844#[derive(Debug, Serialize, Deserialize)]
2845#[serde(rename_all = "camelCase")]
2846pub struct CloseWorkspaceArgs {
2847 pub actor: User,
2848 pub workspace: String,
2849 #[serde(default)]
2850 pub dry_run: bool,
2851}
2852
2853/// `cancel_subscription` (`resume` false) ends a plan at the end of the
2854/// period paid for; with `resume` true, takes that back. Owners only.
2855/// Returns `Outcome<FeatureState>`.
2856#[derive(Debug, Serialize, Deserialize)]
2857pub struct CancelSubscriptionArgs {
2858 pub actor: User,
2859 pub workspace: String,
2860 pub feature: Feature,
2861 #[serde(default)]
2862 pub resume: bool,
2863}
2864
2865/// `has_feature`: whether a feature works for a workspace now, asked by the
2866/// service that provides it before doing paid work. Returns
2867/// `Outcome<bool>`: a failure, with the reason to show, when it does not.
2868/// True everywhere when no card processor is configured.
2869#[derive(Debug, Serialize, Deserialize)]
2870pub struct HasFeatureArgs {
2871 pub workspace: String,
2872 pub feature: Feature,
2873}
2874
2875/// `free_workspaces`: which of `workspaces` are free, that is on no paid
2876/// plan. Paid is the g1t plan, an enterprise's terms, or a discount of
2877/// 100% (g1t's own workspaces). Identity asks before a workspace is made
2878/// (a person owns at most one free workspace) and before anyone is added
2879/// to one (a free workspace cannot invite). Returns `Vec<String>`, the
2880/// free ones, lower-cased; none where payments are not set up.
2881#[derive(Debug, Serialize, Deserialize)]
2882pub struct FreeWorkspacesArgs {
2883 pub workspaces: Vec<String>,
2884}
2885
2886/// `charge_feature`: usage of a feature past its plan's allowance, charged
2887/// from the workspace's credit at cost plus the margin, whatever
2888/// `FREE_WHILE_BUILDING` says. Called by the service that provides it.
2889/// Charged once per `reference`. Returns `Outcome<bool>`: false if that
2890/// reference was charged before.
2891#[derive(Debug, Serialize, Deserialize)]
2892#[serde(rename_all = "camelCase")]
2893pub struct ChargeFeatureArgs {
2894 pub workspace: String,
2895 pub feature: Feature,
2896 /// What it cost g1t, in millionths of a dollar, before the margin.
2897 pub cost_micros: i64,
2898 pub description: String,
2899 /// `namespace/name`, when the usage was one repository's.
2900 pub repo: Option<String>,
2901 /// Unique to this charge, e.g. `deployments/acme/2026-10`.
2902 pub reference: String,
2903 /// For a build: how long it ran. The plan's included build time this
2904 /// month pays for what it can, and only the rest of `cost_micros` is
2905 /// charged.
2906 #[serde(default)]
2907 pub build_seconds: Option<u32>,
2908}
2909
2910// ---------------------------------------------------------------------
2911// Costs and margin: what Cloudflare charges g1t against what g1t
2912// charges (billing's costs.rs, margin.rs and pricing.rs). Staff only.
2913// ---------------------------------------------------------------------
2914
2915/// `admin_costs`: the Costs & margin page. Returns `CostsReport`.
2916#[derive(Debug, Default, Serialize, Deserialize)]
2917pub struct AdminCostsArgs {
2918 /// How many days back, 7 to 90; 30 when absent.
2919 #[serde(default)]
2920 pub days: Option<u32>,
2921}
2922
2923/// One of g1t's products on one day.
2924#[derive(Clone, Debug, Default, Serialize, Deserialize)]
2925#[serde(rename_all = "camelCase")]
2926pub struct CostDay {
2927 pub day: String,
2928 pub bucket: String,
2929 /// What Cloudflare charged g1t.
2930 pub cf_cost_micros: i64,
2931 /// What g1t's meters recorded it cost, at the price book's cost.
2932 pub own_cost_micros: i64,
2933 /// What customers were charged for it at price, before included
2934 /// usage, trials and pools paid for some.
2935 pub value_micros: i64,
2936 /// Of that, what workspaces paid.
2937 pub cash_micros: i64,
2938}
2939
2940/// One product over the range.
2941#[derive(Clone, Debug, Default, Serialize, Deserialize)]
2942#[serde(rename_all = "camelCase")]
2943pub struct ProductMargin {
2944 pub bucket: String,
2945 pub title: String,
2946 /// The cost the margin is taken from: Cloudflare's bill, or g1t's own
2947 /// figure for what Cloudflare does not bill (models).
2948 pub cost_micros: i64,
2949 pub cf_cost_micros: i64,
2950 pub own_cost_micros: i64,
2951 pub value_micros: i64,
2952 pub margin_micros: i64,
2953 pub margin_percent: Option<f64>,
2954 /// `cloudflare` or `ledger`.
2955 pub cost_source: String,
2956 /// Running g1t itself, paid for by the plan.
2957 pub overhead: bool,
2958}
2959
2960/// All of g1t over the range: money in against every cost, and against
2961/// the cost of what was sold (every cost less what g1t gave away).
2962#[derive(Clone, Debug, Default, Serialize, Deserialize)]
2963#[serde(rename_all = "camelCase")]
2964pub struct OverallMargin {
2965 /// What workspaces paid for usage, and for the plan.
2966 pub usage_micros: i64,
2967 pub plans_micros: i64,
2968 pub cost_micros: i64,
2969 pub margin_micros: i64,
2970 pub margin_percent: Option<f64>,
2971 /// Of `cost_micros`, what went on usage g1t gave away on purpose:
2972 /// comped workspaces, free periods, the trial and the open-source pool.
2973 #[serde(default)]
2974 pub given_micros: i64,
2975 /// Money in against `cost_micros - given_micros`.
2976 #[serde(default)]
2977 pub sold_margin_micros: i64,
2978 #[serde(default)]
2979 pub sold_margin_percent: Option<f64>,
2980 /// What was sold, apart: usage (`usage_micros` against what that usage
2981 /// cost, less what was given), running g1t (`plans_micros` against the
2982 /// platform's cost, less its given share) and what no mapping names.
2983 #[serde(default)]
2984 pub usage_cost_micros: i64,
2985 #[serde(default)]
2986 pub usage_margin_micros: i64,
2987 #[serde(default)]
2988 pub usage_margin_percent: Option<f64>,
2989 #[serde(default)]
2990 pub running_cost_micros: i64,
2991 #[serde(default)]
2992 pub unmapped_cost_micros: i64,
2993 /// `given_micros` by why: comped workspaces, free use (free periods,
2994 /// free allowances, overruns g1t covered), the trial, the open-source pool.
2995 #[serde(default)]
2996 pub given_comped_micros: i64,
2997 #[serde(default)]
2998 pub given_free_micros: i64,
2999 #[serde(default)]
3000 pub given_trial_micros: i64,
3001 #[serde(default)]
3002 pub given_pool_micros: i64,
3003 /// What discounts on an account's terms took below cost plus the
3004 /// margin: given, so a discounted sale is not margin lost.
3005 #[serde(default)]
3006 pub given_discount_micros: i64,
3007 /// Credits from g1t spent on usage, by kind: given, so usage paid for
3008 /// with them is never money in. Refunds are not here: they come off
3009 /// money in on the day they refund.
3010 #[serde(default)]
3011 pub given_credit_promotional_micros: i64,
3012 #[serde(default)]
3013 pub given_credit_goodwill_micros: i64,
3014 /// What testing resets wiped that g1t paid for (`reset_costs`): the
3015 /// model calls and Cloudflare usage still happened, so their cost is
3016 /// given, never a leak.
3017 #[serde(default)]
3018 pub given_reset_micros: i64,
3019 /// What workspaces were charged while payments were not live (Stripe's
3020 /// test mode): no real money came in, so it is given, never money in.
3021 #[serde(default)]
3022 pub given_unpaid_micros: i64,
3023 /// Credits over the range: given (every kind), spent on usage, and
3024 /// refunds' money given back.
3025 #[serde(default)]
3026 pub credits_given_micros: i64,
3027 #[serde(default)]
3028 pub credits_used_micros: i64,
3029 #[serde(default)]
3030 pub credits_refunded_micros: i64,
3031 /// `cost_micros` by who g1t pays: Cloudflare's bill (billed amounts,
3032 /// after the included allowances), and model providers (the ledger's
3033 /// cost of the tokens, which Cloudflare's bill does not show).
3034 /// What the plan's included usage paid for, at price (the ledger's
3035 /// `credit_micros`, comped workspaces left out): money in for usage,
3036 /// paid out of `plans_micros`.
3037 #[serde(default)]
3038 pub included_micros: i64,
3039 #[serde(default)]
3040 pub cloudflare_cost_micros: i64,
3041 #[serde(default)]
3042 pub models_cost_micros: i64,
3043 /// Tax collected with payments over the range, net of refunds: owed to
3044 /// the tax authorities, never in cash or revenue.
3045 #[serde(default)]
3046 pub tax_collected_micros: i64,
3047 /// Card processing fees passed on with card payments from before g1t's
3048 /// prices included them, net of refunds: they paid Stripe's fee, so
3049 /// they are not revenue either.
3050 #[serde(default)]
3051 pub card_fees_micros: i64,
3052 /// Cloudflare's subscriptions over the range: each day's share of the
3053 /// billing cycle it is in (a month's price over the cycle's days), the
3054 /// same accrual g1t's own spend uses for the calendar month.
3055 #[serde(default)]
3056 pub subscriptions_micros: i64,
3057 /// What AI Gateway priced g1t's own provider traffic at over the range
3058 /// (Cloudflare-billed requests left out): what the providers bill, to
3059 /// set beside `models_cost_micros`, the ledger's figure.
3060 #[serde(default)]
3061 pub gateway_cost_micros: i64,
3062}
3063
3064/// A count, cost or leak that does not add up.
3065#[derive(Clone, Debug, Serialize, Deserialize)]
3066#[serde(rename_all = "camelCase")]
3067pub struct CostDrift {
3068 pub bucket: String,
3069 pub title: String,
3070 /// `count` (units g1t counted against Cloudflare's), `cost` (the bill
3071 /// against the price book's cost of the same usage; for models, what AI
3072 /// Gateway priced g1t's provider traffic at against the ledger's model
3073 /// cost), `unpriced` (model usage AI Gateway put no price on, so its
3074 /// cost is not the providers'), or `leak`.
3075 pub kind: String,
3076 pub ours: f64,
3077 pub cloudflare: f64,
3078 pub delta_percent: Option<f64>,
3079 pub detail: String,
3080 pub found_at: String,
3081}
3082
3083/// A margin alert, open while its condition lasts.
3084#[derive(Clone, Debug, Serialize, Deserialize)]
3085#[serde(rename_all = "camelCase")]
3086pub struct MarginAlert {
3087 pub id: String,
3088 /// `margin`, `overall`, `leak`, `drift` or `workspace`.
3089 pub kind: String,
3090 /// The product, or the workspace.
3091 pub subject: String,
3092 pub detail: String,
3093 pub since: String,
3094 pub opened_at: String,
3095 pub emailed_at: Option<String>,
3096}
3097
3098/// A change to a price the reconciler measured.
3099#[derive(Clone, Debug, Serialize, Deserialize)]
3100#[serde(rename_all = "camelCase")]
3101pub struct PriceProposal {
3102 pub id: String,
3103 pub meter: String,
3104 pub title: String,
3105 pub unit: String,
3106 pub current_cost_micros: f64,
3107 pub proposed_cost_micros: f64,
3108 pub change_percent: f64,
3109 pub markup_percent: u32,
3110 pub reason: String,
3111 /// `keeper` or `reconciler`.
3112 pub source: String,
3113 /// Far off the current cost: look before approving.
3114 pub suspect: bool,
3115 /// `open`, `applied`, `approved`, `rejected` or `superseded`.
3116 pub status: String,
3117 pub created_at: String,
3118 pub decided_at: Option<String>,
3119 pub decided_by: Option<String>,
3120 pub note: Option<String>,
3121 /// When it takes or took effect, once approved or applied.
3122 pub effective_at: Option<String>,
3123}
3124
3125/// One version of one meter's price. Never changed once written.
3126#[derive(Clone, Debug, Serialize, Deserialize)]
3127#[serde(rename_all = "camelCase")]
3128pub struct PriceVersion {
3129 pub id: String,
3130 pub meter: String,
3131 pub version: u32,
3132 pub cost_micros: f64,
3133 pub markup_percent: u32,
3134 pub price_micros: f64,
3135 pub effective_at: String,
3136 pub reason: String,
3137 pub created_by: String,
3138 /// When the price book took it on; absent while it waits for its date.
3139 pub applied_at: Option<String>,
3140 /// What `cost_micros` is: `cost`, what g1t pays for a unit (a
3141 /// provider's dollar passed on at cost is one); `rate`, a price g1t
3142 /// sets with no cost behind it (the agent rate, security activation),
3143 /// so its cost column is its price; `weight`, a multiplier in
3144 /// millionths, not money (the agent rate's token weights).
3145 #[serde(default)]
3146 pub basis: String,
3147}
3148
3149/// What a workspace cost g1t over the range, Cloudflare's costs shared
3150/// out by g1t's own meters, against what it paid.
3151#[derive(Clone, Debug, Serialize, Deserialize)]
3152#[serde(rename_all = "camelCase")]
3153pub struct WorkspaceCost {
3154 pub workspace: String,
3155 pub cost_micros: i64,
3156 pub revenue_micros: i64,
3157 /// Of `cost_micros`, what g1t gave away.
3158 #[serde(default)]
3159 pub given_micros: i64,
3160 /// One of g1t's own (comped) workspaces.
3161 pub internal: bool,
3162}
3163
3164/// One Cloudflare meter over the range, and the product it is a cost of.
3165#[derive(Clone, Debug, Serialize, Deserialize)]
3166#[serde(rename_all = "camelCase")]
3167pub struct CostLineSummary {
3168 pub product: String,
3169 pub meter: String,
3170 pub raw_name: String,
3171 pub unit: String,
3172 pub source: String,
3173 pub quantity: f64,
3174 pub cost_micros: i64,
3175 /// Absent when no mapping claims it.
3176 pub bucket: Option<String>,
3177}
3178
3179/// A row of the mapping from Cloudflare's meters to g1t's products.
3180#[derive(Clone, Debug, Serialize, Deserialize)]
3181#[serde(rename_all = "camelCase")]
3182pub struct CostMapping {
3183 pub product: String,
3184 pub meter: String,
3185 pub bucket: String,
3186 pub price_meter: Option<String>,
3187 pub own_meter: Option<String>,
3188 pub scale_to_own: bool,
3189 pub drift_percent: f64,
3190 pub note: String,
3191 pub updated_at: String,
3192 pub updated_by: String,
3193}
3194
3195/// The guardrails on prices and the alerts.
3196#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
3197#[serde(rename_all = "camelCase")]
3198pub struct CostSettings {
3199 /// Apply small moves without staff.
3200 pub auto_apply: bool,
3201 /// The largest move applied without staff, either way, in percent.
3202 pub auto_apply_percent: f64,
3203 /// Days between telling customers of a rise and charging it.
3204 pub notice_days: u32,
3205 /// Below this margin, in percent, for `alert_days` days in a row, alert.
3206 pub margin_floor_percent: f64,
3207 pub alert_days: u32,
3208 /// Days with less cost than this say nothing about a margin.
3209 pub min_daily_cost_micros: i64,
3210 /// A workspace costing more than its revenue times this, over 30 days,
3211 /// and at least `anomaly_floor_micros`, is flagged.
3212 pub anomaly_factor: f64,
3213 pub anomaly_floor_micros: i64,
3214}
3215
3216impl Default for CostSettings {
3217 fn default() -> Self {
3218 CostSettings {
3219 auto_apply: true,
3220 auto_apply_percent: 25.0,
3221 notice_days: 14,
3222 margin_floor_percent: 10.0,
3223 alert_days: 3,
3224 min_daily_cost_micros: 100_000,
3225 anomaly_factor: 1.0,
3226 anomaly_floor_micros: 1_000_000,
3227 }
3228 }
3229}
3230
3231/// The Costs & margin page.
3232#[derive(Clone, Debug, Default, Serialize, Deserialize)]
3233#[serde(rename_all = "camelCase")]
3234pub struct CostsReport {
3235 /// A token to read Cloudflare's bill is set.
3236 pub configured: bool,
3237 /// When Cloudflare's bill was last read.
3238 pub fetched_at: Option<String>,
3239 /// The days shown, YYYY-MM-DD.
3240 pub since: String,
3241 pub until: String,
3242 pub days: Vec<CostDay>,
3243 pub products: Vec<ProductMargin>,
3244 pub overall: OverallMargin,
3245 pub drift: Vec<CostDrift>,
3246 pub alerts: Vec<MarginAlert>,
3247 pub proposals: Vec<PriceProposal>,
3248 pub versions: Vec<PriceVersion>,
3249 pub top_workspaces: Vec<WorkspaceCost>,
3250 pub lines: Vec<CostLineSummary>,
3251 pub mappings: Vec<CostMapping>,
3252 pub settings: CostSettings,
3253 /// g1t's own spend against its two caps.
3254 #[serde(default)]
3255 pub caps: SpendCaps,
3256 /// Cloudflare's current billing cycle: its usage cost so far, by meter,
3257 /// and where it is heading. Absent until the bill has been read.
3258 #[serde(default)]
3259 pub cycle: Option<CloudflareCycle>,
3260 /// The last read of Cloudflare's billable usage: what came back.
3261 #[serde(default)]
3262 pub bill_read: Option<BillRead>,
3263 /// Of the range's cost, what no workspace's usage could carry (a day
3264 /// with no usage at all): running g1t, attributed to no one. The
3265 /// workspaces' costs and this add up to the cost.
3266 #[serde(default)]
3267 pub unattributed_micros: i64,
3268}
3269
3270/// Cloudflare's billing cycle (monthly, from the day the account's
3271/// subscription renews), as Cloudflare's Billable usage page shows it.
3272#[derive(Clone, Debug, Default, Serialize, Deserialize)]
3273#[serde(rename_all = "camelCase")]
3274pub struct CloudflareCycle {
3275 /// The cycle's first and last days, YYYY-MM-DD, UTC.
3276 pub start: String,
3277 pub end: String,
3278 pub days: u32,
3279 /// Days from its start to today, today included.
3280 pub days_elapsed: u32,
3281 /// Usage cost so far, after the included allowances.
3282 pub usage_micros: i64,
3283 /// `usage_micros` over the days elapsed, times the cycle's days.
3284 pub projected_micros: i64,
3285 pub average_daily_micros: i64,
3286 /// Cloudflare's subscriptions for the cycle (not on the usage bill).
3287 pub subscriptions_micros: i64,
3288 pub meters: Vec<CycleMeter>,
3289}
3290
3291/// One of Cloudflare's meters over the cycle so far.
3292#[derive(Clone, Debug, Default, Serialize, Deserialize)]
3293#[serde(rename_all = "camelCase")]
3294pub struct CycleMeter {
3295 pub product: String,
3296 pub meter: String,
3297 pub raw_name: String,
3298 pub unit: String,
3299 /// What was used.
3300 pub quantity: f64,
3301 /// What the cycle includes, in the same unit; None without a list price.
3302 pub included: Option<f64>,
3303 /// Past the included amount, as Cloudflare bills it.
3304 pub billable_quantity: f64,
3305 pub cost_micros: i64,
3306 /// `cloudflare` (the cost Cloudflare put on its lines), `list` (the
3307 /// list price past the included amount, while Cloudflare's lines carry
3308 /// no cost), or `none` (no list price known: costed at $0).
3309 pub basis: String,
3310}
3311
3312/// What the last read of Cloudflare's billable usage got back.
3313#[derive(Clone, Debug, Default, Serialize, Deserialize)]
3314#[serde(rename_all = "camelCase")]
3315pub struct BillRead {
3316 pub read_at: String,
3317 pub since: String,
3318 pub until: String,
3319 pub rows: u32,
3320 pub pages: u32,
3321 /// Rows with a consumed quantity (`ConsumedQuantity`), and rows with
3322 /// only a pricing quantity.
3323 pub consumed_rows: u32,
3324 pub pricing_only_rows: u32,
3325 /// Rows Cloudflare put a cost on.
3326 pub costed_rows: u32,
3327}
3328
3329/// What g1t itself pays for, against its caps (billing's `budget`): the
3330/// daily breaker on all of it, and each comped account's monthly budget.
3331/// One of Cloudflare's subscriptions, at what it comes to a month.
3332#[derive(Clone, Debug, Default, PartialEq, Serialize, Deserialize)]
3333#[serde(rename_all = "camelCase")]
3334pub struct FixedCost {
3335 pub name: String,
3336 pub monthly_micros: i64,
3337}
3338
3339/// At cost, never at price. What sudo's Costs page and its red bar show.
3340#[derive(Clone, Debug, Default, Serialize, Deserialize)]
3341#[serde(rename_all = "camelCase")]
3342pub struct SpendCaps {
3343 /// Today (UTC), YYYY-MM-DD, and this month, YYYY-MM.
3344 pub day: String,
3345 pub month: String,
3346 /// What g1t paid for itself today across every workspace: comped work,
3347 /// the trial and open-source pools, free workspaces' overruns, and
3348 /// anything charged without real money behind it.
3349 pub today_micros: i64,
3350 /// `PLATFORM_DAILY_SPEND_CAP_MICROS`. Zero: no breaker.
3351 pub daily_cap_micros: i64,
3352 /// The breaker is open: new compute that g1t would pay
3353 /// for wait until tomorrow (UTC) or until staff lift it.
3354 pub tripped: bool,
3355 pub tripped_at: Option<String>,
3356 /// Staff lifted it for the rest of the day.
3357 pub lifted_by: Option<String>,
3358 pub lifted_at: Option<String>,
3359 pub lift_note: Option<String>,
3360 /// This month so far, by what paid: `comped`, `trial`, `oss`, `given`,
3361 /// `unpaid`.
3362 pub month_buckets: Vec<SpendBucket>,
3363 /// Each comped account's monthly budget.
3364 pub comped: Vec<CompedBudget>,
3365 /// Free workspaces' share of this month's reconciled costs (git,
3366 /// storage, platform), through yesterday.
3367 pub free_tier_micros: i64,
3368 /// Cloudflare's subscriptions a month: as read from Cloudflare each
3369 /// day, else `CLOUDFLARE_FIXED_MONTHLY_MICROS`, an estimate.
3370 pub fixed_monthly_micros: i64,
3371 /// `cloudflare` or `estimate`.
3372 #[serde(default)]
3373 pub fixed_source: String,
3374 #[serde(default)]
3375 pub fixed_read_at: Option<String>,
3376 /// Each subscription, when read from Cloudflare.
3377 #[serde(default)]
3378 pub fixed_items: Vec<FixedCost>,
3379 /// Of `fixed_monthly_micros`, this calendar month's days so far: each
3380 /// day's share of the billing cycle it is in, today included.
3381 #[serde(default)]
3382 pub fixed_month_micros: i64,
3383 /// Money in this month, through the last reconciled day.
3384 pub revenue_micros: i64,
3385 /// Of this month's buckets, what was spent on workspaces whose billing
3386 /// a testing reset later wiped: still g1t's spend, but no longer on
3387 /// their ledger, so the reconciled figures have it only where the
3388 /// reset kept it (as given away, testing resets).
3389 #[serde(default)]
3390 pub reset_micros: i64,
3391 /// Those workspaces.
3392 #[serde(default)]
3393 pub reset_workspaces: Vec<String>,
3394}
3395
3396#[derive(Clone, Debug, Default, Serialize, Deserialize)]
3397#[serde(rename_all = "camelCase")]
3398pub struct SpendBucket {
3399 pub bucket: String,
3400 pub title: String,
3401 pub micros: i64,
3402}
3403
3404/// A comped account's monthly budget: what its work cost g1t this month.
3405#[derive(Clone, Debug, Default, Serialize, Deserialize)]
3406#[serde(rename_all = "camelCase")]
3407pub struct CompedBudget {
3408 pub account: String,
3409 pub name: String,
3410 pub used_micros: i64,
3411 /// Zero: no budget.
3412 pub ceiling_micros: i64,
3413 /// The ceiling is `COMPED_MONTHLY_CEILING_MICROS`, not the account's own.
3414 pub default_ceiling: bool,
3415 /// 50, 75, 90, 100, or 0.
3416 pub level: u32,
3417}
3418
3419/// `admin_spend_caps`: g1t's own spend against its caps. Returns `SpendCaps`.
3420#[derive(Debug, Default, Serialize, Deserialize)]
3421pub struct AdminSpendCapsArgs {}
3422
3423/// `admin_lift_breaker`: lets runs g1t pays for start again for the rest
3424/// of today (UTC), with why. Recorded in the audit log. Returns
3425/// `Outcome<SpendCaps>`.
3426#[derive(Debug, Serialize, Deserialize)]
3427pub struct AdminLiftBreakerArgs {
3428 pub note: String,
3429 pub by: String,
3430}
3431
3432// ---- Platform pauses and the usage watcher ----
3433// docs.g1t.sh/guides/deploy-to-cloudflare/#spend-guardrails
3434
3435/// A g1t-wide pause, set by staff in sudo or by billing's hourly usage
3436/// watcher on a severe breach. Each level is independent.
3437#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize)]
3438#[serde(rename_all = "snake_case")]
3439pub enum PauseLevel {
3440 /// Agents, sandboxes, Actions hosted jobs, deploy builds: every
3441 /// reservation through billing's `reserve` but embeddings.
3442 Compute,
3443 /// Actions' cron-triggered runs, and the runner's sweep that starts
3444 /// queued agents.
3445 Schedules,
3446 /// Context embeddings and backfills, and search's backfills.
3447 Indexing,
3448 /// Social card rendering, which falls back to a static image.
3449 Renders,
3450}
3451
3452impl PauseLevel {
3453 pub const ALL: [PauseLevel; 4] = [PauseLevel::Compute, PauseLevel::Schedules, PauseLevel::Indexing, PauseLevel::Renders];
3454
3455 pub fn as_str(self) -> &'static str {
3456 match self {
3457 PauseLevel::Compute => "compute",
3458 PauseLevel::Schedules => "schedules",
3459 PauseLevel::Indexing => "indexing",
3460 PauseLevel::Renders => "renders",
3461 }
3462 }
3463
3464 pub fn parse(text: &str) -> Option<PauseLevel> {
3465 PauseLevel::ALL.into_iter().find(|level| level.as_str() == text.trim())
3466 }
3467}
3468
3469/// `platform_pause`: which levels are paused now. Takes nothing. Callers
3470/// keep the answer about 30 seconds; one that cannot read it runs (fails
3471/// open).
3472#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, Serialize, Deserialize)]
3473pub struct PlatformPause {
3474 #[serde(default)]
3475 pub compute: bool,
3476 #[serde(default)]
3477 pub schedules: bool,
3478 #[serde(default)]
3479 pub indexing: bool,
3480 #[serde(default)]
3481 pub renders: bool,
3482}
3483
3484impl PlatformPause {
3485 pub fn is(&self, level: PauseLevel) -> bool {
3486 match level {
3487 PauseLevel::Compute => self.compute,
3488 PauseLevel::Schedules => self.schedules,
3489 PauseLevel::Indexing => self.indexing,
3490 PauseLevel::Renders => self.renders,
3491 }
3492 }
3493
3494 pub fn set(&mut self, level: PauseLevel, paused: bool) {
3495 match level {
3496 PauseLevel::Compute => self.compute = paused,
3497 PauseLevel::Schedules => self.schedules = paused,
3498 PauseLevel::Indexing => self.indexing = paused,
3499 PauseLevel::Renders => self.renders = paused,
3500 }
3501 }
3502
3503 pub fn any(&self) -> bool {
3504 PauseLevel::ALL.into_iter().any(|level| self.is(level))
3505 }
3506}
3507
3508/// One level as sudo shows it: who paused it, when and why.
3509#[derive(Clone, Debug, Default, Serialize, Deserialize)]
3510pub struct PauseState {
3511 pub level: String,
3512 pub paused: bool,
3513 pub note: Option<String>,
3514 pub set_by: Option<String>,
3515 pub set_at: Option<String>,
3516 /// Set by the usage watcher, not a person.
3517 pub auto: bool,
3518}
3519
3520/// One metric's usage over an hour or the month so far.
3521#[derive(Clone, Debug, Default, Serialize, Deserialize)]
3522pub struct PlatformMetric {
3523 pub metric: String,
3524 pub title: String,
3525 pub value: f64,
3526 /// Its hourly threshold (`PLATFORM_HOURLY_*`); zero: none.
3527 pub threshold: f64,
3528 /// The script, queue, database or namespace that counted most.
3529 pub top_name: Option<String>,
3530 pub top_value: Option<f64>,
3531}
3532
3533/// A breach the watcher found.
3534#[derive(Clone, Debug, Default, Serialize, Deserialize)]
3535pub struct PlatformBreach {
3536 pub id: String,
3537 pub metric: String,
3538 pub hour: String,
3539 /// `threshold` or `spike`.
3540 pub rule: String,
3541 pub value: f64,
3542 pub threshold: f64,
3543 pub severe: bool,
3544 pub top_name: Option<String>,
3545 pub detail: String,
3546 /// Levels it paused.
3547 pub paused: Vec<String>,
3548 pub opened_at: String,
3549 pub emailed_at: Option<String>,
3550}
3551
3552/// `admin_platform_guard`: the pauses, the last hour read, the month so
3553/// far and the last day's breaches, for sudo's Costs page and its banner.
3554#[derive(Clone, Debug, Default, Serialize, Deserialize)]
3555pub struct PlatformGuard {
3556 pub levels: Vec<PauseState>,
3557 /// The last hour the watcher read (`YYYY-MM-DDTHH:00:00Z`), if any.
3558 pub hour: Option<String>,
3559 pub last_hour: Vec<PlatformMetric>,
3560 pub month: String,
3561 pub month_to_date: Vec<PlatformMetric>,
3562 /// The last 24 hours' breaches, newest first.
3563 pub breaches: Vec<PlatformBreach>,
3564 /// Whether the watcher can read Cloudflare's analytics at all.
3565 pub can_read: bool,
3566 /// `AUTO_PAUSE`: the levels a severe breach may pause.
3567 pub auto_pause: Vec<String>,
3568 /// What the latest run could not see: each query that failed.
3569 #[serde(default)]
3570 pub blind: Vec<BlindQuery>,
3571 /// The latest run found every dataset empty (the wrong account, or a
3572 /// token that cannot see its analytics).
3573 #[serde(default)]
3574 pub empty: bool,
3575 /// The hour the latest run read.
3576 #[serde(default)]
3577 pub last_run: Option<String>,
3578}
3579
3580/// A query the watcher's latest run could not read.
3581#[derive(Clone, Debug, Default, Serialize, Deserialize)]
3582pub struct BlindQuery {
3583 pub key: String,
3584 pub dataset: String,
3585 pub error: String,
3586}
3587
3588/// `admin_platform_guard`. Returns `PlatformGuard`.
3589#[derive(Debug, Default, Serialize, Deserialize)]
3590pub struct AdminPlatformGuardArgs {}
3591
3592/// `admin_set_pause`: pauses or resumes one level, with why. Recorded in
3593/// the audit log. Returns `Outcome<PlatformGuard>`.
3594#[derive(Debug, Serialize, Deserialize)]
3595pub struct AdminSetPauseArgs {
3596 pub level: String,
3597 pub paused: bool,
3598 pub note: String,
3599 pub by: String,
3600}
3601
3602/// `admin_cost_alerts`: the open margin alerts, for sudo's banner.
3603/// Returns `Vec<MarginAlert>`.
3604#[derive(Debug, Default, Serialize, Deserialize)]
3605pub struct AdminCostAlertsArgs {}
3606
3607/// `admin_decide_proposal`: approve or reject a price proposal. An
3608/// approved rise takes effect after the notice period. Returns
3609/// `Outcome<PriceProposal>`.
3610#[derive(Debug, Serialize, Deserialize)]
3611pub struct AdminDecideProposalArgs {
3612 pub id: String,
3613 /// `approve` or `reject`.
3614 pub decision: String,
3615 #[serde(default)]
3616 pub note: String,
3617 pub by: String,
3618}
3619
3620/// `admin_set_cost_settings`. Returns `Outcome<CostSettings>`.
3621#[derive(Debug, Serialize, Deserialize)]
3622pub struct AdminSetCostSettingsArgs {
3623 pub settings: CostSettings,
3624 pub by: String,
3625}
3626
3627/// `admin_set_cost_mapping`: adds, changes or (with `remove`) removes a
3628/// mapping row. Returns `Outcome<CostMapping>`.
3629#[derive(Debug, Serialize, Deserialize)]
3630pub struct AdminSetCostMappingArgs {
3631 pub product: String,
3632 pub meter: String,
3633 #[serde(default)]
3634 pub bucket: String,
3635 #[serde(default)]
3636 pub price_meter: Option<String>,
3637 #[serde(default)]
3638 pub own_meter: Option<String>,
3639 #[serde(default)]
3640 pub scale_to_own: bool,
3641 #[serde(default)]
3642 pub drift_percent: Option<f64>,
3643 #[serde(default)]
3644 pub note: String,
3645 #[serde(default)]
3646 pub remove: bool,
3647 pub by: String,
3648}
3649
3650/// `admin_run_costs`: reads Cloudflare's bill and reconciles now, as the
3651/// daily run does. Returns `Outcome<CostsRun>`.
3652#[derive(Debug, Default, Serialize, Deserialize)]
3653pub struct AdminRunCostsArgs {
3654 #[serde(default)]
3655 pub by: String,
3656}
3657
3658#[derive(Clone, Debug, Default, Serialize, Deserialize)]
3659#[serde(rename_all = "camelCase")]
3660pub struct CostsRun {
3661 pub lines: u32,
3662 pub days: u32,
3663 pub proposals: u32,
3664 pub alerts: u32,
3665 /// What could not be read, in words.
3666 pub problems: Vec<String>,
3667}
3668
3669// --- The Usage page ----------------------------------------------------------
3670
3671/// The product families the Usage page groups meters into, in order, with
3672/// their names.
3673pub const PRODUCTS: [(&str, &str); 7] = [
3674 ("sandboxes", "Sandboxes"),
3675 ("deployments", "Deployments"),
3676 ("git_storage", "Git & storage"),
3677 ("packages", "Packages"),
3678 ("security", "Security & quality"),
3679 ("search", "Search"),
3680 ("observability", "Observability"),
3681];
3682
3683/// `usage_report`: a workspace's usage over a range of days, at price, by
3684/// product, meter, project and day. The figures are the ledger's: the same
3685/// lines the statement and invoices read, so every page agrees. Members
3686/// only. Returns `Outcome<UsageReport>`.
3687#[derive(Debug, Serialize, Deserialize)]
3688#[serde(rename_all = "camelCase")]
3689pub struct UsageReportArgs {
3690 pub workspace: String,
3691 pub viewer: Viewer,
3692 /// The first day, `YYYY-MM-DD` (UTC).
3693 pub from: String,
3694 /// The last day, `YYYY-MM-DD`, included.
3695 pub until: String,
3696 /// Only these product families (`agent`, `sandboxes`…); all when empty.
3697 #[serde(default)]
3698 pub products: Vec<String>,
3699 /// Only these projects (repositories, `owner/name`); all when empty.
3700 #[serde(default)]
3701 pub projects: Vec<String>,
3702}
3703
3704/// What usage came to over a range, and what paid for it. `price_micros`
3705/// less `discount_micros`, `included_micros` and `credits_micros` is
3706/// `charged_micros`.
3707#[derive(Clone, Debug, Default, PartialEq, Serialize, Deserialize)]
3708#[serde(rename_all = "camelCase")]
3709pub struct UsageTotals {
3710 /// Usage at price, metered usage not yet charged (`pending_micros`)
3711 /// included.
3712 pub price_micros: i64,
3713 /// What the account's discount took off.
3714 pub discount_micros: i64,
3715 /// What the plan's included usage, the trial and g1t's pools paid.
3716 pub included_micros: i64,
3717 /// What credit paid: credit from g1t, and AI credit bought before g1t
3718 /// stopped selling it.
3719 pub credits_micros: i64,
3720 /// What is left for the workspace to pay.
3721 pub charged_micros: i64,
3722 /// Metered this month and charged when it closes (storage, git
3723 /// operations, scans, embeddings, domains), at price.
3724 pub pending_micros: i64,
3725 /// What of `pending_micros` the workspace will be charged when the
3726 /// month closes: its price less what g1t covers and what the discount
3727 /// takes off, as the close will enter it. Credit comes off at the close.
3728 #[serde(default)]
3729 pub pending_charged_micros: i64,
3730 /// What it cost g1t, before any markup.
3731 pub cost_micros: i64,
3732}
3733
3734/// One day's usage of one product, at price.
3735#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
3736#[serde(rename_all = "camelCase")]
3737pub struct UsageDay {
3738 /// `YYYY-MM-DD`.
3739 pub day: String,
3740 pub product: String,
3741 pub micros: i64,
3742}
3743
3744/// How much of an allowance is used, in the meter's unit.
3745#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
3746#[serde(rename_all = "camelCase")]
3747pub struct Allowance {
3748 pub used: f64,
3749 pub of: f64,
3750 /// `bytes`, `operations`, `dollars`…
3751 pub unit: String,
3752}
3753
3754/// One project's part of a meter.
3755#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
3756#[serde(rename_all = "camelCase")]
3757pub struct ProjectUsage {
3758 /// `owner/name`, or empty for usage that is not one project's.
3759 pub project: String,
3760 pub micros: i64,
3761 pub quantity: f64,
3762}
3763
3764/// One meter over the range.
3765#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
3766#[serde(rename_all = "camelCase")]
3767pub struct MeterLine {
3768 /// `agent_models`, `agent_rate`, `sandbox`, `builds`…
3769 pub key: String,
3770 pub label: String,
3771 pub product: String,
3772 /// What `quantity` counts: `tokens`, `seconds`, `bytes`, `operations`,
3773 /// `entries`.
3774 pub unit: String,
3775 pub quantity: f64,
3776 /// At price.
3777 pub micros: i64,
3778 /// Of `micros`, metered this month and charged when it closes.
3779 #[serde(default)]
3780 pub pending_micros: i64,
3781 /// Every day of the range, oldest first, at price: the sparkline.
3782 pub daily: Vec<i64>,
3783 #[serde(default)]
3784 pub allowance: Option<Allowance>,
3785 pub by_project: Vec<ProjectUsage>,
3786 /// How the quantity is counted, when that needs saying: for the agent
3787 /// rate, its tokens are weighted by kind, and this names the weights.
3788 #[serde(default)]
3789 pub note: Option<String>,
3790}
3791
3792/// One product family over the range.
3793#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
3794#[serde(rename_all = "camelCase")]
3795pub struct ProductUsage {
3796 pub key: String,
3797 pub label: String,
3798 pub micros: i64,
3799 pub meters: Vec<MeterLine>,
3800}
3801
3802#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
3803#[serde(rename_all = "camelCase")]
3804pub struct UsageReport {
3805 pub from: String,
3806 pub until: String,
3807 pub totals: UsageTotals,
3808 /// Each day and product with usage, oldest first.
3809 pub days: Vec<UsageDay>,
3810 /// Every product family, in order, even with nothing used.
3811 pub products: Vec<ProductUsage>,
3812 /// Every project with usage in the range, for the filter.
3813 pub projects: Vec<String>,
3814 /// The plan's included usage this month, when the workspace has it.
3815 #[serde(default)]
3816 pub included: Option<Allowance>,
3817 /// The account's discount, in percent, when it has one.
3818 #[serde(default)]
3819 pub discount_percent: Option<u32>,
3820 /// AI credit bought before g1t stopped selling it, left now (kept
3821 /// apart: it pays only for usage no longer offered), and credit from
3822 /// g1t for everything.
3823 pub ai_credit_micros: i64,
3824 pub credit_micros: i64,
3825 /// The trial credit left, for a workspace on its trial.
3826 #[serde(default)]
3827 pub trial_micros: Option<i64>,
3828 pub plan: PlanKind,
3829 /// Nothing is charged while g1t is being built out.
3830 pub free: bool,
3831}
3832
3833// --- Budgets ------------------------------------------------------------------
3834
3835/// `set_budget`: the monthly budget on usage after included usage: the
3836/// owners' spend limit, its alerts, whether usage pauses at 100%, and an
3837/// optional webhook. Owners only. Returns `Outcome<Limit>`.
3838#[derive(Debug, Serialize, Deserialize)]
3839#[serde(rename_all = "camelCase")]
3840pub struct SetBudgetArgs {
3841 pub actor: User,
3842 pub workspace: String,
3843 /// The amount; None keeps the automatic one.
3844 #[serde(default, alias = "amount_micros")]
3845 pub amount_micros: Option<i64>,
3846 /// Some of 50, 75, 90 and 100.
3847 #[serde(default)]
3848 pub alerts: Vec<u32>,
3849 #[serde(default = "yes", alias = "pause_at_limit")]
3850 pub pause_at_limit: bool,
3851 /// An HTTPS address, or None for no webhook.
3852 #[serde(default)]
3853 pub webhook: Option<String>,
3854 /// Leave the spend limit as it is and change only the alerts, the
3855 /// pause and the webhook.
3856 #[serde(default, alias = "keep_limit")]
3857 pub keep_limit: bool,
3858}
3859
3860// --- Billing details -----------------------------------------------------------
3861
3862/// A postal address, as Stripe keeps it.
3863#[derive(Clone, Debug, Default, PartialEq, Serialize, Deserialize)]
3864#[serde(rename_all = "camelCase")]
3865pub struct PostalAddress {
3866 #[serde(default)]
3867 pub line1: String,
3868 #[serde(default)]
3869 pub line2: String,
3870 #[serde(default)]
3871 pub city: String,
3872 #[serde(default)]
3873 pub state: String,
3874 #[serde(default)]
3875 pub postal_code: String,
3876 /// Two letters, `US`.
3877 #[serde(default)]
3878 pub country: String,
3879}
3880
3881/// The default way the workspace pays, as far as it is safe to show.
3882#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
3883#[serde(rename_all = "camelCase")]
3884pub struct PaymentMethod {
3885 /// `card`, or another kind Stripe has.
3886 pub kind: String,
3887 #[serde(default)]
3888 pub brand: Option<String>,
3889 #[serde(default)]
3890 pub last4: Option<String>,
3891 #[serde(default)]
3892 pub exp_month: Option<u32>,
3893 #[serde(default)]
3894 pub exp_year: Option<u32>,
3895}
3896
3897/// One of the customer's invoices at Stripe: the plan, activations, AI
3898/// credit and month-end usage.
3899#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
3900#[serde(rename_all = "camelCase")]
3901pub struct StripeInvoice {
3902 pub id: String,
3903 #[serde(default)]
3904 pub number: Option<String>,
3905 /// `paid`, `open`, `void`, `uncollectible` or `draft`.
3906 pub status: String,
3907 pub total_cents: i64,
3908 pub currency: String,
3909 /// RFC 3339.
3910 pub created_at: String,
3911 #[serde(default)]
3912 pub description: Option<String>,
3913 #[serde(default)]
3914 pub hosted_url: Option<String>,
3915 #[serde(default)]
3916 pub pdf_url: Option<String>,
3917}
3918
3919/// What the next invoice will be, from g1t's own ledger.
3920#[derive(Clone, Debug, Default, PartialEq, Serialize, Deserialize)]
3921#[serde(rename_all = "camelCase")]
3922pub struct UpcomingInvoice {
3923 /// When the month closes, RFC 3339.
3924 pub closes_at: String,
3925 /// The plan and activations, at their monthly price.
3926 pub subscriptions_micros: i64,
3927 /// Usage still owed, after included usage, credit and any discount.
3928 pub usage_micros: i64,
3929 pub total_micros: i64,
3930}
3931
3932/// `billing_details` (`AccountArgs`): who the invoices are for, the default
3933/// payment method, and the invoices, from the Stripe customer. Owners and
3934/// billing managers see and change it; any other member gets only the
3935/// upcoming invoice and the tax-address notice. Returns
3936/// `Outcome<BillingDetails>`.
3937#[derive(Clone, Debug, Default, PartialEq, Serialize, Deserialize)]
3938#[serde(rename_all = "camelCase")]
3939pub struct BillingDetails {
3940 /// Whether the workspace has a Stripe customer yet.
3941 pub customer: bool,
3942 pub email: Option<String>,
3943 pub name: Option<String>,
3944 pub address: Option<PostalAddress>,
3945 /// `eu_vat`, `us_ein`…, and its value.
3946 pub tax_id_type: Option<String>,
3947 pub tax_id: Option<String>,
3948 /// Printed on invoices.
3949 pub po_number: Option<String>,
3950 /// The invoices' language, such as `en` or `fr`.
3951 pub language: Option<String>,
3952 pub payment_method: Option<PaymentMethod>,
3953 pub invoices: Vec<StripeInvoice>,
3954 pub upcoming: UpcomingInvoice,
3955 /// Stripe could not be read: what is shown is what g1t keeps.
3956 #[serde(default)]
3957 pub unavailable: Option<String>,
3958 /// Whether Stripe Tax can place the customer from the address: tax
3959 /// is worked out from it, and without it nothing is charged.
3960 #[serde(default)]
3961 pub tax_location: bool,
3962 /// Set when g1t did not charge for want of an address (RFC 3339).
3963 #[serde(default)]
3964 pub tax_address_needed_at: Option<String>,
3965 /// Stripe's check of the tax ID: `pending`, `verified`, `unverified` or
3966 /// `unavailable`.
3967 #[serde(default)]
3968 pub tax_id_status: Option<String>,
3969 /// `none`, `exempt` or `reverse`, as staff set it at Stripe; g1t never
3970 /// changes it.
3971 #[serde(default)]
3972 pub tax_exempt: Option<String>,
3973}
3974
3975/// `set_billing_details`: saves the invoice details on the Stripe customer.
3976/// Owners only. Absent fields are left as they are; an empty string clears
3977/// one. Returns `Outcome<BillingDetails>`.
3978#[derive(Debug, Serialize, Deserialize)]
3979#[serde(rename_all = "camelCase")]
3980pub struct SetBillingDetailsArgs {
3981 pub actor: User,
3982 pub workspace: String,
3983 #[serde(default)]
3984 pub email: Option<String>,
3985 #[serde(default)]
3986 pub name: Option<String>,
3987 #[serde(default)]
3988 pub address: Option<PostalAddress>,
3989 #[serde(default, alias = "tax_id_type")]
3990 pub tax_id_type: Option<String>,
3991 #[serde(default, alias = "tax_id")]
3992 pub tax_id: Option<String>,
3993 #[serde(default, alias = "po_number")]
3994 pub po_number: Option<String>,
3995 #[serde(default)]
3996 pub language: Option<String>,
3997}
3998
3999#[cfg(test)]
4000mod tests {
4001 use super::*;
4002
4003 #[test]
4004 fn an_account_carries_no_run_fee() {
4005 let account = Account {
4006 workspace: "acme".into(),
4007 balance_micros: 0,
4008 status: Status { enabled: true, live: false, free: false },
4009 margin_percent: 20,
4010 card: None,
4011 };
4012 let json = serde_json::to_value(account).unwrap();
4013 let mut keys: Vec<&str> = json.as_object().unwrap().keys().map(String::as_str).collect();
4014 keys.sort_unstable();
4015 assert_eq!(keys, ["balanceMicros", "card", "marginPercent", "status", "workspace"]);
4016 }
4017
4018 #[test]
4019 fn a_price_change_says_when_the_markup_moved() {
4020 let change = PriceChange {
4021 meter: "sandbox_second".into(),
4022 old_cost_micros: 21.0,
4023 new_cost_micros: 21.0,
4024 markup_percent: 20,
4025 old_markup_percent: Some(138),
4026 reason: "Sandbox time is now charged at cost plus 20% from the first second".into(),
4027 created_at: "2026-10-05T00:00:00Z".into(),
4028 effective_at: None,
4029 };
4030 assert_eq!(serde_json::to_value(&change).unwrap()["oldMarkupPercent"], 138);
4031 let cost_only = PriceChange { old_markup_percent: None, ..change };
4032 assert!(serde_json::to_value(&cost_only).unwrap().get("oldMarkupPercent").is_none());
4033 }
4034
4035 #[test]
4036 fn features_are_named_as_the_site_sends_them() {
4037 assert_eq!(
4038 serde_json::to_value(Feature::Deployments).unwrap(),
4039 serde_json::json!("deployments")
4040 );
4041 assert_eq!(Feature::parse("deployments"), Some(Feature::Deployments));
4042 assert_eq!(serde_json::to_value(Feature::Plan).unwrap(), serde_json::json!("plan"));
4043 assert_eq!(Feature::parse("plan"), Some(Feature::Plan));
4044 // Older readers named the plan Team.
4045 assert_eq!(Feature::parse("team"), Some(Feature::Plan));
4046 assert_eq!(serde_json::from_value::<Feature>(serde_json::json!("team")).unwrap(), Feature::Plan);
4047 assert_eq!(Feature::ALL, [Feature::Plan, Feature::Security]);
4048 assert_eq!(Feature::parse("security"), Some(Feature::Security));
4049 assert_eq!(serde_json::to_value(Feature::Security).unwrap(), serde_json::json!("security"));
4050 assert!(SubscriptionStatus::Canceling.on());
4051 assert!(!SubscriptionStatus::PastDue.on());
4052 }
4053
4054 #[test]
4055 fn a_reservation_is_asked_for_and_answered_in_camel_case() {
4056 let asked: ReserveArgs = serde_json::from_value(serde_json::json!({
4057 "workspace": "acme",
4058 "repo": { "namespace": "acme", "name": "web" },
4059 "public": true,
4060 "kind": "check",
4061 "estimateMicros": 2_000_000,
4062 }))
4063 .unwrap();
4064 assert_eq!(asked.kind, ComputeKind::Check);
4065 assert!(asked.kind.open_source_pool());
4066 assert!(!ComputeKind::Deploy.open_source_pool());
4067 // Retired kinds still parse from stored rows.
4068 let stored: ComputeKind = serde_json::from_value(serde_json::json!("agent")).unwrap();
4069 assert_eq!(stored, ComputeKind::Agent);
4070 // Rust callers that write snake_case are read too.
4071 let snake: ReserveArgs = serde_json::from_value(serde_json::json!({
4072 "workspace": "acme",
4073 "repo": { "namespace": "acme", "name": "web" },
4074 "public": false,
4075 "kind": "deploy",
4076 "estimate_micros": 1,
4077 }))
4078 .unwrap();
4079 assert_eq!(snake.estimate_micros, 1);
4080 let answer = Reservation { id: "rsv_1".into(), paid_by: PaidBy::OnDemand, held_micros: 5, expires_at: String::new() };
4081 assert_eq!(serde_json::to_value(&answer).unwrap()["paidBy"], "on_demand");
4082 assert_eq!(serde_json::to_value(PlanKind::Internal).unwrap(), "internal");
4083 assert!(!PlanKind::Free.on_demand() && PlanKind::Enterprise.on_demand());
4084 }
4085
4086 #[test]
4087 fn a_refusal_carries_its_own_code() {
4088 let refused: crate::Outcome<Reservation> =
4089 crate::Outcome::fail(crate::FailureCode::OssPoolEmpty, "The open-source pool is spent.");
4090 let json = serde_json::to_value(&refused).unwrap();
4091 assert_eq!(json["error"]["code"], "oss_pool_empty");
4092 assert_eq!(crate::FailureCode::NotPaid.http_status(), 402);
4093 assert_eq!(crate::FailureCode::Paused.http_status(), 409);
4094 }
4095}