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.

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