Skip to content
2,947 linesCodeBlameRaw
1//! The billing service: what agents cost, charged to the workspace they
2//! worked for.
3//!
4//! A workspace buys credit and each agent run deducts what it cost, plus
5//! g1t's margin. With no credit, no agent starts. Money is held in
6//! millionths of a US dollar, so that a run costing a fraction of a cent is
7//! recorded exactly.
8//!
9//! Each `*Args` struct is the argument of the method of the same name,
10//! served at `POST /rpc/<method>`.
11
12use serde::{Deserialize, Serialize};
13
14use crate::repos::RepoPath;
15use crate::{User, Viewer};
16
17/// Millionths of a US dollar in one dollar.
18pub const MICROS_PER_DOLLAR: i64 = 1_000_000;
19
20/// Whether workspaces are charged for agents at all, and with real money.
21/// `status` takes nothing and returns this.
22#[derive(Clone, Copy, Debug, Default, Serialize, Deserialize)]
23pub struct Status {
24 /// False when no payment provider is configured: nothing is charged,
25 /// and who may run agents is decided some other way.
26 pub enabled: bool,
27 /// False while the payment provider is in its test mode, where cards
28 /// are not real.
29 pub live: bool,
30 /// True while g1t is being built out: runs are recorded, with what
31 /// they cost, but nothing is charged and no credit is needed. Not a
32 /// promise that it stays free.
33 #[serde(default)]
34 pub free: bool,
35}
36
37/// `trial`: a workspace's trial credit, so people can try g1t (its agents on
38/// g1t's hosted models among it) without a key or a card of their own. Each
39/// new workspace gets one grant of usage credit (`TRIAL_WORKSPACE_MICROS`),
40/// made when it first uses something, out of a pool for everyone that
41/// resets each calendar month (`TRIAL_MONTHLY_POOL_MICROS`). When this
42/// month's pool is given out, new grants wait for the next month. Returns
43/// `Trial`.
44#[derive(Debug, Serialize, Deserialize)]
45#[serde(rename_all = "camelCase")]
46pub struct TrialArgs {
47 pub workspace: String,
48 /// Workspaces open to hosted models anyway, whose use is not counted
49 /// against the pool.
50 #[serde(default)]
51 pub exempt: Vec<String>,
52}
53
54#[derive(Clone, Debug, Serialize, Deserialize)]
55#[serde(rename_all = "camelCase")]
56pub struct Trial {
57 /// Whether its agents may use g1t's hosted models on the trial now: it
58 /// has credit left, or this month's pool can still grant it some.
59 pub open: bool,
60 /// What the trial has paid for so far, in millionths of a dollar.
61 pub used_micros: i64,
62 /// Its grant, or what it would be granted.
63 pub limit_micros: i64,
64 /// No longer used: the trial does not end on a date. Kept for older
65 /// readers; always null.
66 pub ends_at: Option<String>,
67 /// Why it is closed: `off` (no trials), `used` (this workspace's grant
68 /// is spent) or `pool` (this month's grants are all given out; see
69 /// `waits_until`). `ended` is no longer sent.
70 pub reason: Option<String>,
71 /// Whether the workspace has its grant already.
72 #[serde(default)]
73 pub granted: bool,
74 /// RFC 3339: when a workspace waiting for a grant can get one, the
75 /// first of next month. Only with reason `pool`.
76 #[serde(default)]
77 pub waits_until: Option<String>,
78}
79
80/// A workspace's standing.
81#[derive(Clone, Debug, Serialize, Deserialize)]
82#[serde(rename_all = "camelCase")]
83pub struct Account {
84 pub workspace: String,
85 /// Credit left, in millionths of a dollar. Can dip below zero by the
86 /// cost of the runs that were under way when it ran out.
87 pub balance_micros: i64,
88 pub status: Status,
89 /// What is added to a run's cost, in percent.
90 pub margin_percent: u32,
91 /// The card g1t charges as the workspace nears its limit and when a
92 /// month closes, if one is on file.
93 #[serde(default)]
94 pub card: Option<Card>,
95}
96
97/// A saved card, as far as it is safe to show.
98#[derive(Clone, Debug, Serialize, Deserialize)]
99#[serde(rename_all = "camelCase")]
100pub struct Card {
101 /// `visa`, `mastercard`, ...
102 pub brand: String,
103 pub last4: String,
104 pub exp_month: u32,
105 pub exp_year: u32,
106}
107
108/// `billing_portal`: Stripe's hosted billing page for the workspace, where
109/// an owner adds or replaces the card, sees invoices and receipts, and sets
110/// the billing email and address. g1t never handles card numbers. Owners
111/// only. Returns `Outcome<Checkout>` (its `url`); Stripe sends them back
112/// to `return_url`.
113#[derive(Debug, Serialize, Deserialize)]
114pub struct BillingPortalArgs {
115 pub actor: User,
116 pub workspace: String,
117 pub return_url: String,
118}
119
120/// `admin_billing_link`: for staff to send a customer: their Stripe billing
121/// page. Returns `Outcome<BillingLink>`.
122#[derive(Debug, Serialize, Deserialize)]
123pub struct AdminBillingLinkArgs {
124 pub workspace: String,
125 pub by: String,
126}
127
128#[derive(Clone, Debug, Serialize, Deserialize)]
129#[serde(rename_all = "camelCase")]
130pub struct BillingLink {
131 /// A one-time session on Stripe's billing page, signed in already.
132 pub portal_url: String,
133 /// The billing page's sign-in page, which does not expire: the
134 /// customer signs in with the email Stripe has for them.
135 pub login_url: Option<String>,
136 pub customer_email: Option<String>,
137 pub expires_note: String,
138}
139
140#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize)]
141#[serde(rename_all = "snake_case")]
142pub enum EntryKind {
143 /// Credit bought with a card.
144 TopUp,
145 /// An agent's run, or a paid feature's usage past its allowance.
146 Usage,
147}
148
149/// One line of a workspace's statement.
150#[derive(Clone, Debug, Serialize, Deserialize)]
151#[serde(rename_all = "camelCase")]
152pub struct LedgerEntry {
153 pub id: String,
154 pub kind: EntryKind,
155 /// Positive for credit added, negative for usage.
156 pub amount_micros: i64,
157 pub description: String,
158 /// For usage: the repository and pull request the agent worked on.
159 pub repo: Option<String>,
160 pub number: Option<u32>,
161 /// For usage: `implement`, `review` or `update`.
162 pub task: Option<String>,
163 /// For usage: the model, by its public name.
164 pub model: Option<String>,
165 /// For usage: `g1t` when g1t paid the model provider, `workspace` when
166 /// the workspace's own account did. Runs on the workspace's own
167 /// provider pay only their sandbox time now, so only older entries
168 /// are `workspace`.
169 #[serde(default = "g1t")]
170 pub billed_to: String,
171 /// For a top-up: the username of whoever paid.
172 pub created_by: Option<String>,
173 /// RFC 3339.
174 pub created_at: String,
175 /// The workspace the line belongs to, which tells an enterprise's
176 /// lines apart.
177 #[serde(default, skip_serializing_if = "Option::is_none")]
178 pub workspace: Option<String>,
179 /// For usage: what the g1t plan's monthly included usage paid of it.
180 /// The entry's `amount_micros` is what is left to pay.
181 #[serde(default)]
182 pub credit_micros: i64,
183 /// For usage: what the workspace's trial credit paid of it.
184 #[serde(default)]
185 pub trial_micros: i64,
186 /// For usage: what g1t's open-source pool paid of it.
187 #[serde(default)]
188 pub oss_micros: i64,
189 /// For usage: what g1t covered itself, such as the part of a free
190 /// workspace's last trial run that went past its trial credit.
191 #[serde(default)]
192 pub given_micros: i64,
193}
194
195fn g1t() -> String {
196 "g1t".to_owned()
197}
198
199/// `account` (`Outcome<Account>`) and `ledger` (`Outcome<Vec<LedgerEntry>>`,
200/// newest first). Members of the workspace only.
201#[derive(Debug, Serialize, Deserialize)]
202pub struct AccountArgs {
203 pub workspace: String,
204 pub viewer: Viewer,
205}
206
207/// `checkout`: prepays usage: money paid in advance, drawn down by usage
208/// after the plan's included usage, which raises what can be used before
209/// work stops by the same amount at once. $25 at the least. By card, with
210/// 3-D Secure; from $1,000 also by bank transfer. Owners of the workspace
211/// only. Returns `Outcome<Checkout>`.
212#[derive(Debug, Serialize, Deserialize)]
213#[serde(rename_all = "camelCase")]
214pub struct CheckoutArgs {
215 pub actor: User,
216 pub workspace: String,
217 /// How much to prepay, in cents.
218 pub amount_cents: u32,
219 /// Where the payment page sends the person afterwards. The payment's
220 /// id is appended as `session`.
221 pub return_url: String,
222 /// `card` (the default) or `bank_transfer` (from $1,000): Stripe gives
223 /// the account details, and the money counts once it arrives.
224 #[serde(default)]
225 pub method: Option<String>,
226}
227
228#[derive(Debug, Serialize, Deserialize)]
229pub struct Checkout {
230 /// The payment page to send the person to.
231 pub url: String,
232}
233
234/// `confirm`: credits a payment once the provider says it was made. Safe
235/// to call any number of times. Returns `Outcome<Account>`.
236#[derive(Debug, Serialize, Deserialize)]
237pub struct ConfirmArgs {
238 pub workspace: String,
239 pub viewer: Viewer,
240 /// The payment's id, as returned to `return_url`.
241 pub session: String,
242}
243
244/// `can_start`: whether a workspace may start an agent now, asked before
245/// anything is opened for it. Returns `Outcome<bool>`: a failure, with the
246/// reason to show, when it has no credit.
247#[derive(Debug, Serialize, Deserialize)]
248pub struct CanStartArgs {
249 pub workspace: String,
250}
251
252/// `start_run`: asks whether a workspace may start an agent, and opens the
253/// run it will be charged for. Called by the runner service. Returns
254/// `Outcome<Option<RunTicket>>`: no ticket when billing is off, a failure
255/// when the workspace has no credit.
256#[derive(Debug, Serialize, Deserialize)]
257pub struct StartRunArgs {
258 pub workspace: String,
259 pub repo: RepoPath,
260 pub number: u32,
261 /// `implement`, `review` or `update`.
262 pub task: String,
263 /// The model, by its public name.
264 pub model: String,
265 /// `workspace` when the run uses the workspace's own model provider.
266 /// The runner, which is TypeScript, sends it as `billedTo`.
267 #[serde(default = "g1t", alias = "billedTo")]
268 pub billed_to: String,
269 /// The model session's id, when its requests go through g1t's AI
270 /// Gateway: settling charges the run what the gateway priced them at.
271 #[serde(default)]
272 pub session: Option<String>,
273 /// `small` or `large`: the tier g1t routed the run to, when g1t pays
274 /// for its model. None on the workspace's own provider.
275 #[serde(default)]
276 pub tier: Option<String>,
277}
278
279#[derive(Clone, Debug, Serialize, Deserialize)]
280#[serde(rename_all = "camelCase")]
281pub struct RunTicket {
282 pub run_id: String,
283 /// Lets the sandbox, and nothing else, report what this run cost.
284 pub token: String,
285}
286
287/// `finish_run`: what a run cost, as its sandbox reports it. Charged once.
288/// Returns `Outcome<bool>`.
289#[derive(Debug, Serialize, Deserialize)]
290#[serde(rename_all = "camelCase")]
291pub struct FinishRunArgs {
292 pub run_id: String,
293 pub token: String,
294 /// What the model provider charged, in US dollars.
295 pub cost_usd: f64,
296 #[serde(default)]
297 pub turns: u32,
298}
299
300
301/// `usage`: what a workspace's agents cost over a period, broken down.
302/// Members only. Returns `Outcome<Usage>`.
303#[derive(Debug, Serialize, Deserialize)]
304pub struct UsageArgs {
305 pub workspace: String,
306 pub viewer: Viewer,
307 /// RFC 3339: the start of the period. The period runs to now.
308 pub since: String,
309}
310
311/// One slice of usage: what it was for, what it cost, how many runs.
312#[derive(Clone, Debug, Serialize, Deserialize)]
313#[serde(rename_all = "camelCase")]
314pub struct UsageSlice {
315 pub key: String,
316 pub micros: i64,
317 pub runs: u32,
318}
319
320/// What a workspace's agents cost over a period.
321#[derive(Clone, Debug, Serialize, Deserialize)]
322#[serde(rename_all = "camelCase")]
323pub struct Usage {
324 pub since: String,
325 /// Charged, including g1t's margin.
326 pub spent_micros: i64,
327 /// What g1t's usage came to at price, less what was charged: the plan's
328 /// included usage, the trial, a pool or a free period paid it. Usage at
329 /// price is `spent_micros` plus this.
330 #[serde(default)]
331 pub covered_micros: i64,
332 /// What g1t's model provider charged, before the margin.
333 pub cost_micros: i64,
334 /// What runs on the workspace's own provider cost there, as the harness
335 /// estimated it. Not charged by g1t.
336 pub provider_micros: i64,
337 /// What the runs used, at cost: g1t's models and the workspace's own
338 /// provider together, whatever was charged for them.
339 pub used_micros: i64,
340 /// g1t charges nothing for now. The slices then measure usage at cost,
341 /// since every charge is zero.
342 pub free: bool,
343 pub runs: u32,
344 /// Spend per day (`YYYY-MM-DD`) and task, as `day/task` keys.
345 pub by_day: Vec<UsageSlice>,
346 /// Per task: implement, review, revise, update, plan.
347 pub by_task: Vec<UsageSlice>,
348 /// Per repository, `namespace/name`.
349 pub by_repo: Vec<UsageSlice>,
350 /// The pull requests that cost most, as `namespace/name#number`.
351 pub by_pull: Vec<UsageSlice>,
352 /// Per model, by its public name.
353 pub by_model: Vec<UsageSlice>,
354 /// Credit bought in the period.
355 pub added_micros: i64,
356}
357
358/// `record_tokens`: what one model answer used, added to the day's count
359/// for its run. The model proxy sends it after each answer. For usage
360/// views only: runs are still priced from AI Gateway. Returns
361/// `Outcome<bool>`: false when there was nothing to count.
362#[derive(Debug, Serialize, Deserialize)]
363#[serde(rename_all = "camelCase")]
364pub struct RecordTokensArgs {
365 pub workspace: String,
366 /// The model session's id (`ModelSession::id`), one per run.
367 pub session: String,
368 /// The person the run is for, by username. Absent when nobody asked.
369 #[serde(default)]
370 pub person: Option<String>,
371 pub model: String,
372 /// On g1t's hosted models: `small` or `large`.
373 #[serde(default)]
374 pub tier: Option<String>,
375 #[serde(default)]
376 pub input: u64,
377 #[serde(default)]
378 pub output: u64,
379 #[serde(default)]
380 pub cache_read: u64,
381 #[serde(default)]
382 pub cache_write: u64,
383}
384
385/// `token_usage`: the model tokens a workspace's runs used, day by day,
386/// for the whole workspace or for one person. Members only; a member may
387/// ask only for themselves, an owner for anyone. Returns
388/// `Outcome<TokenUsage>`.
389#[derive(Debug, Serialize, Deserialize)]
390pub struct TokenUsageArgs {
391 pub workspace: String,
392 pub viewer: Viewer,
393 /// A username: only the runs for them.
394 #[serde(default)]
395 pub person: Option<String>,
396 /// How many days, to today: 42 when absent, 366 at most.
397 #[serde(default)]
398 pub days: Option<u32>,
399}
400
401/// One day's tokens.
402#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
403pub struct DayTokens {
404 /// `YYYY-MM-DD`, UTC.
405 pub day: String,
406 pub tokens: u64,
407}
408
409/// The model tokens runs used over a window of days.
410#[derive(Clone, Debug, Serialize, Deserialize)]
411#[serde(rename_all = "camelCase")]
412pub struct TokenUsage {
413 /// `YYYY-MM-DD`: the first day counted.
414 pub since: String,
415 pub days: u32,
416 /// Null for the whole workspace.
417 pub person: Option<String>,
418 pub total_tokens: u64,
419 pub input_tokens: u64,
420 pub output_tokens: u64,
421 pub cache_read_tokens: u64,
422 pub cache_write_tokens: u64,
423 /// What those runs were charged, as `usage` measures it.
424 pub cost_micros: i64,
425 /// Days in the window with any tokens.
426 pub active_days: u32,
427 /// Every day in the window, oldest first, zeros included.
428 pub by_day: Vec<DayTokens>,
429}
430
431/// What a workspace pays a monthly price for. There is one plan, `plan`
432/// ("g1t"): a flat price per workspace, never per person, with included
433/// usage each month, more private storage, and deployments. Never free:
434/// `FREE_WHILE_BUILDING` does not cover it.
435///
436/// `deployments` is not sold on its own any more: it comes with the plan.
437/// A service that asks `has_feature` for it is told whether the workspace
438/// has the plan, and a Deployments subscription bought before the change
439/// keeps working until its period ends.
440#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize)]
441#[serde(rename_all = "snake_case")]
442pub enum Feature {
443 /// The g1t plan. Older readers called it `team`.
444 #[serde(alias = "team")]
445 Plan,
446 /// Previews per pull request and production on g1t.page: part of the
447 /// plan.
448 Deployments,
449 /// The Security and quality activation: the security suite's paid
450 /// features on private repositories, for a monthly price per workspace
451 /// from the price book (`security_activation`). Sold on its own; it
452 /// does not need the plan, and the plan does not include it.
453 Security,
454}
455
456impl Feature {
457 /// What is sold: the plan, and the Security and quality activation.
458 pub const ALL: [Feature; 2] = [Feature::Plan, Feature::Security];
459
460 pub fn as_str(self) -> &'static str {
461 match self {
462 Feature::Plan => "plan",
463 Feature::Deployments => "deployments",
464 Feature::Security => "security",
465 }
466 }
467
468 pub fn parse(name: &str) -> Option<Feature> {
469 match name {
470 "plan" | "team" => Some(Feature::Plan),
471 "deployments" => Some(Feature::Deployments),
472 "security" => Some(Feature::Security),
473 _ => None,
474 }
475 }
476
477 pub fn title(self) -> &'static str {
478 match self {
479 Feature::Plan => "g1t",
480 Feature::Deployments => "Deployments",
481 Feature::Security => "Security and quality",
482 }
483 }
484}
485
486/// What deployments cost g1t, in millionths of a dollar: fallbacks for
487/// when billing's price book cannot be read. Nothing here is an allowance:
488/// on the plan every unit is metered from the first, at cost plus the
489/// margin, and drawn from the plan's included usage before anything is
490/// charged. Projects, previews and the apps behind them are not metered at
491/// all: Cloudflare's Workers for Platforms includes far more scripts than
492/// g1t runs, so an app costs g1t only the requests and CPU it answers with.
493pub mod deployment_costs {
494 /// Workers for Platforms: $0.30 per million requests.
495 pub const MICROS_PER_MILLION_REQUESTS: i64 = 300_000;
496 /// $0.02 per million CPU milliseconds.
497 pub const MICROS_PER_MILLION_CPU_MS: i64 = 20_000;
498 /// What one second of a build's sandbox costs g1t (Cloudflare
499 /// Containers, standard-1: half a vCPU, 4 GiB, 8 GB disk), rounded up,
500 /// as the price keeper measured it on 2026-10-05 (14.5). Only a
501 /// fallback: billing charges builds at the price book's `build_second`,
502 /// which the keeper keeps current.
503 pub const MICROS_PER_BUILD_SECOND: i64 = 15;
504 /// What one custom hostname costs g1t a month (Cloudflare for SaaS):
505 /// $0.10.
506 pub const MICROS_PER_DOMAIN_MONTH: i64 = 100_000;
507}
508
509/// `record_sandbox`: how long one sandbox ran for a workspace, reported by
510/// the runner when it stops. Every sandbox g1t starts for a workspace
511/// (agents, reviews, checks, the merge queue, workflow jobs) is metered by
512/// the second, from the first: recorded once per `reference`, with what it
513/// cost g1t, and charged at the price book's `sandbox_second` price unless
514/// `FREE_WHILE_BUILDING`. Deploy builds are charged by the Deployments plan
515/// instead.
516/// Returns `Outcome<bool>`: false if that reference was recorded before.
517#[derive(Debug, Serialize, Deserialize)]
518#[serde(rename_all = "camelCase")]
519pub struct RecordSandboxArgs {
520 pub workspace: String,
521 pub seconds: u32,
522 /// What ran, e.g. `Checks on acme/api#12`.
523 pub description: String,
524 /// `namespace/name`.
525 pub repo: Option<String>,
526 /// Unique to the run.
527 pub reference: String,
528 /// What ran: `agent`, `check`, `workflow` or `queue`. Decides whether
529 /// g1t's open-source pool may pay for it (checks, workflows and the
530 /// merge queue on public repositories). Absent: not the pool.
531 #[serde(default)]
532 pub kind: Option<ComputeKind>,
533 /// The vCPU-seconds the sandbox used, when it can tell. With it, the
534 /// run is priced on its own CPU (`sandbox_base_second` per second plus
535 /// `sandbox_cpu_second` per vCPU-second); without it, at the average
536 /// (`sandbox_second`).
537 #[serde(default, alias = "cpu_seconds")]
538 pub cpu_seconds: Option<f64>,
539 /// The reservation the work started under, settled with this cost.
540 #[serde(default, alias = "reservation_id")]
541 pub reservation_id: Option<String>,
542 /// It ran on one of the workspace's self-hosted runners: recorded as
543 /// self-hosted time, for the minutes, at $0.
544 #[serde(default, alias = "self_hosted")]
545 pub self_hosted: bool,
546 /// The machine it ran on, by label (`g1t-4core`); absent, the standard
547 /// one. A larger machine's memory and disk cost more each second.
548 #[serde(default)]
549 pub instance: Option<String>,
550}
551
552/// How much a workspace has earned g1t's trust with money, which sets how
553/// far its unpaid usage can go before its work stops.
554#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize)]
555#[serde(rename_all = "snake_case")]
556pub enum Trust {
557 /// No live payment yet: only a little past the free allowances.
558 New,
559 /// Has paid g1t real money: the ceiling grows with what it has paid.
560 Paid,
561 /// Has paid steadily for months, with nothing disputed or declined:
562 /// the ceiling follows its monthly spend, up to $10,000, by itself.
563 Established,
564 /// A ceiling g1t set by hand, after talking to the workspace.
565 Reviewed,
566 /// g1t's own workspaces: no ceiling.
567 Internal,
568}
569
570#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize)]
571#[serde(rename_all = "snake_case")]
572pub enum LimitState {
573 Ok,
574 /// Past 80% of the ceiling.
575 Warning,
576 /// At or past it: no new sandboxes, builds or app requests.
577 Stopped,
578}
579
580/// How far a workspace's unpaid usage has gone this month, and where its
581/// work stops: like Fly's or Cloudflare's limits for new accounts, so no
582/// one runs up costs g1t cannot collect. Usage counts at what it cost g1t
583/// or what it is charged, whichever is more, so it counts while g1t is
584/// free too.
585#[derive(Clone, Debug, Serialize, Deserialize)]
586#[serde(rename_all = "camelCase")]
587pub struct Limit {
588 pub workspace: String,
589 /// The account that pays, whose usage and payments the limit counts:
590 /// the workspace's own, or its enterprise's.
591 #[serde(default)]
592 pub account: String,
593 #[serde(default)]
594 pub account_name: String,
595 pub trust: Trust,
596 /// Usage this month (UTC) less what was paid this month.
597 pub exposure_micros: i64,
598 /// Where work stops: the lower of g1t's ceiling and the owner's own
599 /// spend limit. None for g1t's own workspaces.
600 pub ceiling_micros: Option<i64>,
601 /// The ceiling g1t sets from `trust`.
602 pub trust_ceiling_micros: Option<i64>,
603 /// The owner's own monthly limit, if they set one.
604 pub spend_limit_micros: Option<i64>,
605 pub state: LimitState,
606 /// What to tell people when work is stopped or close to it.
607 pub message: Option<String>,
608 /// Charged this month, which the spend limit is measured against.
609 #[serde(default)]
610 pub spent_micros: i64,
611 /// True while the owners have not chosen a spend limit of their own, so
612 /// the automatic one applies: $200, or twice last month's spend.
613 #[serde(default)]
614 pub default_spend_limit: bool,
615 /// The most the owners may set their own limit to: g1t's ceiling. To
616 /// go past it, they contact g1t.
617 #[serde(default)]
618 pub available_micros: Option<i64>,
619 /// How the ceiling grows from here, in a sentence.
620 #[serde(default)]
621 pub growth: Option<String>,
622 /// Money paid in advance and not used yet. It raises what can be used
623 /// before work stops by the same amount, at once.
624 #[serde(default)]
625 pub prepaid_micros: i64,
626 /// The highest ceiling the workspace has ever had. Owners may set their
627 /// spend limit anywhere up to it (plus what is prepaid) without asking.
628 #[serde(default)]
629 pub max_ceiling_micros: Option<i64>,
630 /// The most the owners may raise the limit to themselves, once, with
631 /// `raise_once`: twice the highest ceiling. None once it is used.
632 #[serde(default)]
633 pub raise_once_micros: Option<i64>,
634 /// When the one-time raise was used, RFC 3339.
635 #[serde(default)]
636 pub raised_at: Option<String>,
637 /// True in a paid workspace's first billing cycle, when the ceiling is
638 /// the starting one (`LIMIT_PAID_START_MICROS`).
639 #[serde(default)]
640 pub first_month: bool,
641}
642
643/// `limit`: a workspace's limit, for its members. Returns `Outcome<Limit>`.
644#[derive(Debug, Serialize, Deserialize)]
645pub struct LimitArgs {
646 pub workspace: String,
647 pub viewer: Viewer,
648}
649
650/// `check_limit`: the same, for the services that enforce it. Returns
651/// `Outcome<Limit>`.
652#[derive(Debug, Serialize, Deserialize)]
653pub struct CheckLimitArgs {
654 pub workspace: String,
655}
656
657/// `note_pending`: usage this month that will be charged later, such as
658/// app traffic past a plan, so the workspace's limit counts it now. Each
659/// report replaces the last for that workspace, source and month. Called
660/// by the service that meters it. Returns `bool`.
661#[derive(Debug, Serialize, Deserialize)]
662#[serde(rename_all = "camelCase")]
663pub struct NotePendingArgs {
664 pub workspace: String,
665 /// `deployments`, `security` (scans), `context` (search embeddings),
666 /// `storage` or `cache` (actions/cache, plan only). Billing charges
667 /// `security`, `context`, `storage` and `cache` itself once the month
668 /// is over; `deployments` charges its own.
669 pub source: String,
670 /// What it cost g1t so far this month, before the margin.
671 pub cost_micros: i64,
672 /// How much of it, for the Billing page: `1.2 million requests and
673 /// 3.4 million CPU ms`, `2 custom domains`.
674 #[serde(default)]
675 pub detail: Option<String>,
676}
677
678/// `usage_meters`: this month's usage for a workspace, one line per kind
679/// of meter, at what it is charged (cost plus the margin, on the account's
680/// terms) before the plan's included usage, the trial or g1t's pools paid
681/// for any of it. Members only. Returns `Outcome<Vec<MeterUsage>>`.
682#[derive(Debug, Serialize, Deserialize)]
683pub struct UsageMetersArgs {
684 pub workspace: String,
685 pub viewer: Viewer,
686}
687
688/// One kind of meter's usage this month.
689#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
690#[serde(rename_all = "camelCase")]
691pub struct MeterUsage {
692 /// `agents` (agent runs, models and sandboxes for checks, workflows
693 /// and the merge queue), `builds`, `requests` (app requests and CPU),
694 /// `domains`, `git_storage` (git operations and private storage) or
695 /// `search_scans` (search embeddings and security scans).
696 pub key: String,
697 pub label: String,
698 /// At price, before what paid for it.
699 pub micros: i64,
700 /// How much, when it is known: `12 runs`, `41 build minutes`.
701 #[serde(default)]
702 pub quantity: Option<String>,
703}
704
705/// `set_spend_limit`: the owner's own monthly ceiling, under g1t's; None
706/// removes it. Owners only. Returns `Outcome<Limit>`.
707#[derive(Debug, Serialize, Deserialize)]
708#[serde(rename_all = "camelCase")]
709pub struct SetSpendLimitArgs {
710 pub actor: User,
711 pub workspace: String,
712 /// A monthly limit, at most what is available; None goes back to the
713 /// default.
714 pub spend_limit_micros: Option<i64>,
715 /// Use everything available, with no limit of their own.
716 #[serde(default)]
717 pub use_full_limit: bool,
718 /// Use the one-time raise: up to twice the highest ceiling the
719 /// workspace has had, without asking. Once per workspace.
720 #[serde(default, alias = "raiseOnce")]
721 pub raise_once: bool,
722}
723
724/// One metered unit: what it costs g1t, and what it is sold at. The price
725/// is always `cost × (100 + markup) / 100`, so it follows the cost.
726#[derive(Clone, Debug, Serialize, Deserialize)]
727#[serde(rename_all = "camelCase")]
728pub struct Price {
729 /// `sandbox_second`, `build_second`, `app_requests`, `app_cpu`, `app_month`.
730 pub meter: String,
731 pub title: String,
732 pub unit: String,
733 /// Millionths of a dollar per unit; may have a fraction.
734 pub cost_micros: f64,
735 pub markup_percent: u32,
736 pub price_micros: f64,
737 /// `list`: Cloudflare's published price. `cloudflare`: what Cloudflare
738 /// actually billed g1t, measured.
739 pub source: String,
740 /// When it was last checked against Cloudflare's bill.
741 pub checked_at: Option<String>,
742 pub updated_at: String,
743}
744
745impl Price {
746 pub fn price_for(cost_micros: f64, markup_percent: u32) -> f64 {
747 cost_micros * f64::from(100 + markup_percent) / 100.0
748 }
749}
750
751/// A cost that moved.
752#[derive(Clone, Debug, Serialize, Deserialize)]
753#[serde(rename_all = "camelCase")]
754pub struct PriceChange {
755 pub meter: String,
756 pub old_cost_micros: f64,
757 pub new_cost_micros: f64,
758 pub markup_percent: u32,
759 /// The markup before, when the change was to the markup rather than
760 /// to the cost. Absent when the markup stayed `markup_percent`.
761 #[serde(default, skip_serializing_if = "Option::is_none")]
762 pub old_markup_percent: Option<u32>,
763 pub reason: String,
764 pub created_at: String,
765 /// When a change still to come takes effect: a rise is announced
766 /// before it is charged. Absent for changes already made.
767 #[serde(default, skip_serializing_if = "Option::is_none")]
768 pub effective_at: Option<String>,
769}
770
771/// `prices`: every metered price and the recent changes. Public. Returns
772/// `PriceBook`.
773#[derive(Clone, Debug, Serialize, Deserialize)]
774#[serde(rename_all = "camelCase")]
775pub struct PriceBook {
776 pub prices: Vec<Price>,
777 pub changes: Vec<PriceChange>,
778 /// The margin on model usage, which is charged at what AI Gateway
779 /// priced each request at.
780 pub model_margin_percent: u32,
781 /// Every plan, as it is sold now.
782 #[serde(default)]
783 pub plans: Vec<Plan>,
784 /// What is free, and what pays for it.
785 #[serde(default)]
786 pub free: Option<FreeTier>,
787}
788
789/// What g1t gives without a plan, each with what pays for it: a capped
790/// budget, never an open-ended allowance.
791#[derive(Clone, Debug, Default, Serialize, Deserialize)]
792#[serde(rename_all = "camelCase")]
793pub struct FreeTier {
794 /// Each new workspace's trial credit, once.
795 pub trial_workspace_micros: i64,
796 /// Trial grants each month, in all; new trials wait when it is spent.
797 pub trial_monthly_pool_micros: i64,
798 /// g1t's open-source pool each month, and any one repository's share.
799 pub oss_pool_micros: i64,
800 pub oss_repo_micros: i64,
801 /// Private repository storage that is free for every workspace. Past
802 /// it, the plan pays at cost plus the margin; a free workspace's pushes
803 /// to private repositories stop instead.
804 pub free_private_storage_bytes: i64,
805 /// Days of audit log a free workspace keeps.
806 pub audit_retention_days: u32,
807 /// Days of audit log the g1t plan keeps, and g1t's own and enterprise
808 /// workspaces. Longer is by arrangement, set per account in sudo.
809 #[serde(default)]
810 pub plan_audit_retention_days: u32,
811 /// The smallest amount a card is charged when a month closes; less
812 /// carries over. Charges at a limit always go through.
813 pub min_charge_micros: i64,
814 /// Git operations (clones, fetches and pushes through g1t) that are
815 /// free for every workspace each month. Past it, the plan pays at cost
816 /// plus the margin and is never slowed; a free workspace is slowed
817 /// down, never charged.
818 #[serde(default)]
819 pub git_operations_included: u64,
820 /// A new paid workspace's ceiling in its first month.
821 #[serde(default)]
822 pub paid_start_ceiling_micros: i64,
823 /// The most a one-click goodwill credit can cost g1t.
824 #[serde(default)]
825 pub overage_forgive_cost_micros: i64,
826}
827
828/// Who pays: a billing account. Every workspace has one; by default its
829/// own. An enterprise account pays for several workspaces at once, as
830/// GitHub Enterprise does: one bill, one limit, one set of terms.
831#[derive(Clone, Debug, Serialize, Deserialize)]
832#[serde(rename_all = "camelCase")]
833pub struct BillingAccount {
834 /// `ws_<slug>` for a workspace's own account; `ent_…` for an enterprise.
835 pub id: String,
836 pub kind: AccountKind,
837 pub name: String,
838 pub terms: Terms,
839 /// The workspaces it pays for.
840 pub workspaces: Vec<String>,
841 /// Where an enterprise's invoices go.
842 #[serde(default)]
843 pub billing_email: Option<String>,
844 /// An enterprise's invoices, newest first. Empty for a workspace's own.
845 #[serde(default)]
846 pub invoices: Vec<EnterpriseInvoice>,
847 pub created_at: String,
848 /// What g1t staff set for the account beyond its terms.
849 #[serde(default)]
850 pub allowances: Allowances,
851}
852
853/// Set per account by g1t staff in sudo, on top of its terms.
854#[derive(Clone, Debug, Default, PartialEq, Serialize, Deserialize)]
855#[serde(rename_all = "camelCase")]
856pub struct Allowances {
857 /// The g1t plan without paying for its monthly price, such as for a
858 /// partner. Usage is charged as usual. Comped accounts have it anyway.
859 #[serde(default, alias = "team")]
860 pub plan: bool,
861 /// Each of the account's public repositories' monthly cap on g1t's
862 /// open-source pool, in place of `OSS_REPO_MICROS`. None: the default.
863 #[serde(default)]
864 pub oss_repo_micros: Option<i64>,
865 /// The trial credit each of its workspaces gets, in place of
866 /// `TRIAL_WORKSPACE_MICROS`, outside the monthly pool. None: the default.
867 #[serde(default)]
868 pub trial_micros: Option<i64>,
869 /// Agents at once, in place of the plan's (2 in the first month or on
870 /// the trial, then 10). None: the default.
871 #[serde(default)]
872 pub max_concurrent_agents: Option<u32>,
873 /// One run's spend cap, in place of `RUN_CAP_MICROS` and the owners'
874 /// own. None: theirs, or the default.
875 #[serde(default)]
876 pub run_cap_micros: Option<i64>,
877 /// What the agents on one issue may spend in all, in place of
878 /// `ISSUE_CAP_MICROS` and the owners' own. None: theirs, or the default.
879 #[serde(default)]
880 pub issue_cap_micros: Option<i64>,
881 /// Days of audit log its workspaces keep, in place of the plan's (7
882 /// free, 90 on the plan), longer or shorter. None: the plan's.
883 #[serde(default)]
884 pub audit_retention_days: Option<u32>,
885 /// A hold g1t staff put on new compute, with why. None: no hold.
886 #[serde(default)]
887 pub hold: Option<String>,
888}
889
890/// `admin_set_allowances`: the plan on or off without charge, overrides of
891/// the plan's caps, a hold, and the account's share of g1t's pools.
892/// Recorded with who and why. Returns `Outcome<BillingAccount>`.
893#[derive(Debug, Serialize, Deserialize)]
894pub struct AdminSetAllowancesArgs {
895 pub id: String,
896 pub allowances: Allowances,
897 pub note: String,
898 pub by: String,
899}
900
901// --- Entitlements, and compute started under a reservation -----------------
902//
903// Every service that starts compute (sandboxes for agents, checks,
904// workflows and the merge queue; builds; models; semantic search) asks
905// billing first:
906//
907// 1. `entitlements { workspace }` says what the workspace may do at all:
908// its plan, whether it may start compute, its caps, and whether compute
909// is paused.
910// 2. `reserve { workspace, repo, public, kind, estimate_micros }` holds the
911// work's estimated cost against what may pay for it, so that starts at
912// the same moment cannot overshoot the ceiling together. It answers who
913// pays first, or refuses with a stable code and a message for the owner.
914// 3. `settle { reservation_id, actual_micros }` releases the hold once the
915// work is done. The charge itself goes on the ledger the usual way
916// (`finish_run`, `record_sandbox`, `charge_feature`, `note_pending`).
917//
918// A reservation never settled expires after `RESERVATION_HOURS`.
919
920/// A reservation that is never settled stops holding after this long.
921pub const RESERVATION_HOURS: u64 = 3;
922/// What a ceiling reads as when there is none (g1t's own workspaces): a
923/// billion dollars, which JavaScript holds exactly.
924pub const UNLIMITED_MICROS: i64 = 1_000_000_000_000_000;
925
926/// What a workspace pays g1t on, as far as compute is concerned.
927#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize)]
928#[serde(rename_all = "snake_case")]
929pub enum PlanKind {
930 /// No plan: the forge is free; compute only from a trial or g1t's
931 /// open-source pool, after a card check.
932 Free,
933 /// The g1t plan, paid for (or given by g1t staff without its price).
934 Paid,
935 /// g1t's own workspaces and Flagon's (comped terms): the plan without
936 /// being charged. Usage is still recorded at what it cost.
937 Internal,
938 /// Paid for by an enterprise account, invoiced.
939 Enterprise,
940}
941
942impl PlanKind {
943 pub fn as_str(self) -> &'static str {
944 match self {
945 PlanKind::Free => "free",
946 PlanKind::Paid => "paid",
947 PlanKind::Internal => "internal",
948 PlanKind::Enterprise => "enterprise",
949 }
950 }
951
952 /// Whether usage past what is included may be charged (on demand).
953 pub fn on_demand(self) -> bool {
954 !matches!(self, PlanKind::Free)
955 }
956}
957
958/// What compute is for.
959#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize)]
960#[serde(rename_all = "snake_case")]
961pub enum ComputeKind {
962 /// An agent's run: its sandbox and its model.
963 Agent,
964 /// Checks on a pull request.
965 Check,
966 /// A workflow job.
967 Workflow,
968 /// The merge queue's checks.
969 Queue,
970 /// A deployment's build.
971 Deploy,
972 /// Semantic search: embeddings in the context hub.
973 Embedding,
974}
975
976impl ComputeKind {
977 pub fn as_str(self) -> &'static str {
978 match self {
979 ComputeKind::Agent => "agent",
980 ComputeKind::Check => "check",
981 ComputeKind::Workflow => "workflow",
982 ComputeKind::Queue => "queue",
983 ComputeKind::Deploy => "deploy",
984 ComputeKind::Embedding => "embedding",
985 }
986 }
987
988 /// Whether g1t's open-source pool may pay for it on a public
989 /// repository: checks, workflows and the merge queue only.
990 pub fn open_source_pool(self) -> bool {
991 matches!(self, ComputeKind::Check | ComputeKind::Workflow | ComputeKind::Queue)
992 }
993}
994
995/// Who pays first for reserved work. What the first source cannot cover
996/// falls to the next, in this order.
997#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize)]
998#[serde(rename_all = "snake_case")]
999pub enum PaidBy {
1000 /// The plan's included usage this month.
1001 Credit,
1002 /// The workspace's one-time trial credit.
1003 Trial,
1004 /// g1t's open-source pool.
1005 Oss,
1006 /// Charged to the workspace, at cost plus the margin.
1007 OnDemand,
1008}
1009
1010/// `entitlements`: what a workspace may do now, for the services that
1011/// start compute and the pages that show it. Takes `EntitlementsArgs`;
1012/// returns `Entitlements`. No viewer: callers decide who sees it.
1013#[derive(Debug, Serialize, Deserialize)]
1014pub struct EntitlementsArgs {
1015 pub workspace: String,
1016}
1017
1018/// `audit_retention`: how many days of audit log each workspace keeps, for
1019/// the events service's daily purge. Takes `AuditRetentionArgs`; returns
1020/// `Vec<AuditRetention>`, one for each workspace asked about.
1021#[derive(Debug, Serialize, Deserialize)]
1022pub struct AuditRetentionArgs {
1023 pub workspaces: Vec<String>,
1024}
1025
1026#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
1027pub struct AuditRetention {
1028 pub workspace: String,
1029 pub days: u32,
1030}
1031
1032#[derive(Clone, Debug, Serialize, Deserialize)]
1033#[serde(rename_all = "camelCase")]
1034pub struct Entitlements {
1035 pub workspace: String,
1036 pub plan: PlanKind,
1037 /// May start sandboxes, models, deployments and semantic search at all:
1038 /// paid, internal and enterprise workspaces, or a free one with trial
1039 /// credit left. A free workspace may still use the open-source pool
1040 /// for checks, workflows and the merge queue on public repositories
1041 /// after a card check; `reserve` decides that per start.
1042 pub compute: bool,
1043 /// The one-time trial credit left; 0 if none was granted or it is used.
1044 pub trial_micros_left: i64,
1045 /// A card check has been done. The trial and the open-source pool need
1046 /// it.
1047 pub trial_verified: bool,
1048 /// A paid workspace still in its first billing cycle.
1049 pub first_month: bool,
1050 /// Agents at once: 2 in the first month or on the trial, 10 after;
1051 /// staff can override it.
1052 pub max_concurrent_agents: u32,
1053 /// The longest one run may take: 60 minutes in the first month or on
1054 /// the trial; otherwise the guardrails' own caps (`MAX_MINUTES`).
1055 pub max_run_minutes: u32,
1056 /// One run's spend cap (`RUN_CAP_MICROS`, $2 by default); staff can
1057 /// override it.
1058 pub run_cap_micros: i64,
1059 /// What agents may spend on one issue in all (`ISSUE_CAP_MICROS`, $10
1060 /// by default); the owners can set it (`set_caps`), and staff override.
1061 pub issue_cap_micros: i64,
1062 /// Where on-demand work stops: g1t's ceiling on usage not yet paid
1063 /// for. `UNLIMITED_MICROS` for g1t's own workspaces; 0 for a free one,
1064 /// which has no on-demand usage.
1065 pub ceiling_micros: i64,
1066 /// Usage not yet paid for this month, with prepayment taken off.
1067 pub exposure_micros: i64,
1068 /// Why new compute is paused, for the owner: the limit is reached, a
1069 /// spend spike is waiting for an owner to confirm it, or g1t staff put
1070 /// a hold on it. None when it is not.
1071 pub paused: Option<String>,
1072 // What the workspace's plan gives it, for its pages.
1073 /// What open reservations hold now.
1074 #[serde(default)]
1075 pub held_micros: i64,
1076 /// Paid in advance and not used yet.
1077 #[serde(default)]
1078 pub prepaid_micros: i64,
1079 /// The plan's included usage each month, and what of it is used.
1080 #[serde(default)]
1081 pub included_micros: i64,
1082 #[serde(default)]
1083 pub included_used_micros: i64,
1084 /// How far back the audit log can be read and exported, and what is
1085 /// kept: the plan's days, or what g1t staff set for the account.
1086 pub audit_retention_days: u32,
1087 /// Whether `audit_retention_days` is what staff set for the account
1088 /// rather than the plan's.
1089 #[serde(default)]
1090 pub audit_retention_custom: bool,
1091 /// Private repository storage that is free for every workspace: past
1092 /// it, the plan pays for it and a free workspace's pushes stop.
1093 pub free_private_storage_bytes: i64,
1094 /// The last daily measure of the workspace's private repositories.
1095 pub private_storage_bytes: i64,
1096 /// On a paid plan (not Free): storage past the free amounts below is
1097 /// charged, so nothing is refused for it.
1098 #[serde(default)]
1099 pub has_plan: bool,
1100 /// Package storage free for every workspace, public and private: past
1101 /// it, the plan pays for it and a free workspace's pushes are refused.
1102 #[serde(default)]
1103 pub package_public_free_bytes: i64,
1104 #[serde(default)]
1105 pub package_private_free_bytes: i64,
1106 /// What g1t's open-source pool paid for the workspace this month.
1107 pub oss_paid_micros: i64,
1108 /// Deploy build time this month, every second of it metered.
1109 #[serde(default)]
1110 pub build_seconds_used: u32,
1111 /// Git operations this month, and how many are free for every
1112 /// workspace (past it: metered on the plan, slowed when free).
1113 #[serde(default)]
1114 pub git_operations: u64,
1115 #[serde(default)]
1116 pub git_operations_included: u64,
1117 /// The smallest amount a card is charged when a month closes.
1118 pub min_charge_micros: i64,
1119 /// A spend spike waiting for an owner, or decided.
1120 #[serde(default)]
1121 pub spike: Option<Spike>,
1122 /// Where usage stands against what is included and the limits, from 50%.
1123 #[serde(default)]
1124 pub alerts: Vec<UsageAlert>,
1125}
1126
1127/// One level reached: 50, 75, 90 or 100 percent of something.
1128#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
1129#[serde(rename_all = "camelCase")]
1130pub struct UsageAlert {
1131 /// `included` (the plan's included usage), `spend_limit` (the owners'
1132 /// own limit) or `ceiling` (g1t's, on usage not yet paid for).
1133 pub meter: String,
1134 pub level: u32,
1135 pub used_micros: i64,
1136 pub limit_micros: i64,
1137 pub message: String,
1138}
1139
1140/// An hour's spend well above the workspace's usual pace: new compute
1141/// waits until an owner says to keep going.
1142#[derive(Clone, Debug, Serialize, Deserialize)]
1143#[serde(rename_all = "camelCase")]
1144pub struct Spike {
1145 pub id: String,
1146 /// `open` (waiting for an owner), `continued` (an owner said keep
1147 /// going) or `stopped` (an owner said stop).
1148 pub status: String,
1149 /// The hour's spend when it was found, and the usual hour's.
1150 pub hour_micros: i64,
1151 pub average_micros: i64,
1152 pub detected_at: String,
1153 #[serde(default)]
1154 pub decided_by: Option<String>,
1155 #[serde(default)]
1156 pub decided_at: Option<String>,
1157 /// While continued: until when, unless spend doubles again first.
1158 #[serde(default)]
1159 pub until: Option<String>,
1160}
1161
1162/// `reserve`: holds an estimate of a start's cost before the work starts.
1163/// Returns `Outcome<Reservation>`, or a failure whose code says why not:
1164///
1165/// - `paused`: a spend spike waiting for an owner, or a hold.
1166/// - `limit`: the spend limit or g1t's ceiling would be passed.
1167/// - `not_paid`: no plan, and nothing else pays for this kind of work (or
1168/// no card check yet).
1169/// - `trial_used`: the one-time trial is spent.
1170/// - `oss_pool_empty`: the open-source pool, or the repository's share of
1171/// it, is spent this month.
1172///
1173/// The message says exactly what to do, with the page to do it on (such as
1174/// `/acme/-/billing`).
1175#[derive(Debug, Serialize, Deserialize)]
1176#[serde(rename_all = "camelCase")]
1177pub struct ReserveArgs {
1178 pub workspace: String,
1179 pub repo: RepoPath,
1180 /// Whether the repository is public: the open-source pool pays only for
1181 /// public repositories' checks, workflows and merge queue.
1182 pub public: bool,
1183 pub kind: ComputeKind,
1184 /// The most the work is expected to cost g1t, before the margin, in
1185 /// millionths of a dollar (billing adds the margin, as it does to every
1186 /// charge). For an agent, its model's average plus its sandbox for its
1187 /// whole time cap.
1188 #[serde(alias = "estimate_micros")]
1189 pub estimate_micros: i64,
1190 /// An agent run on g1t's hosted models (not the workspace's own
1191 /// provider). Unsaid, an agent run is taken to be one. g1t's daily
1192 /// spend breaker pauses these when g1t is paying for them.
1193 #[serde(default, alias = "hosted_model")]
1194 pub hosted_model: Option<bool>,
1195}
1196
1197#[derive(Clone, Debug, Serialize, Deserialize)]
1198#[serde(rename_all = "camelCase")]
1199pub struct Reservation {
1200 pub id: String,
1201 pub paid_by: PaidBy,
1202 /// What is held, at cost; less than the estimate when a free
1203 /// workspace's last bit of trial credit is all there is.
1204 #[serde(default)]
1205 pub held_micros: i64,
1206 /// RFC 3339: when the hold lapses if never settled.
1207 #[serde(default)]
1208 pub expires_at: String,
1209}
1210
1211/// `settle`: releases a reservation's hold with what the work cost. The
1212/// charge goes on the ledger the usual way. Safe to repeat. Returns
1213/// `Outcome<bool>`: false if it was settled or had lapsed before.
1214#[derive(Debug, Serialize, Deserialize)]
1215#[serde(rename_all = "camelCase")]
1216pub struct SettleArgs {
1217 #[serde(alias = "reservation_id")]
1218 pub reservation_id: String,
1219 /// What the work cost g1t, before the margin.
1220 #[serde(alias = "actual_micros")]
1221 pub actual_micros: i64,
1222}
1223
1224// --- Card checks, the plan, prepayment -------------------------------------
1225
1226/// `card_check`: starts Stripe's page to save and verify a card: a setup
1227/// with 3-D Secure where the card supports it, which the card's bank sees
1228/// as a $0 or $1 authorization that is never charged. The trial and the
1229/// open-source pool need it, and it is the card the plan uses. Owners only.
1230/// Returns `Outcome<Checkout>`; the page's id comes back to `return_url` as
1231/// `session`, for `confirm_card_check`.
1232#[derive(Debug, Serialize, Deserialize)]
1233#[serde(rename_all = "camelCase")]
1234pub struct CardCheckArgs {
1235 pub actor: User,
1236 pub workspace: String,
1237 #[serde(alias = "return_url")]
1238 pub return_url: String,
1239}
1240
1241/// `confirm_card_check`: records the check once Stripe says the card was
1242/// verified, and grants the trial if the month's pool has room and the card
1243/// has not had one before. Safe to repeat. Returns `Outcome<Entitlements>`.
1244#[derive(Debug, Serialize, Deserialize)]
1245pub struct ConfirmCardCheckArgs {
1246 pub workspace: String,
1247 pub viewer: Viewer,
1248 pub session: String,
1249}
1250
1251// --- Limits: raising them, and spikes ---------------------------------------
1252
1253/// A request to g1t: a higher limit, or help with usage that went past
1254/// what was meant.
1255#[derive(Clone, Debug, Serialize, Deserialize)]
1256#[serde(rename_all = "camelCase")]
1257pub struct LimitRequest {
1258 pub id: String,
1259 pub workspace: String,
1260 /// `limit` (raise my limit) or `overage` (spent more than meant to).
1261 pub kind: String,
1262 /// The limit asked for; for an overage, what they think went wrong.
1263 pub amount_micros: i64,
1264 pub reason: String,
1265 pub expected_monthly_micros: i64,
1266 /// `open`, `approved` or `declined`.
1267 pub status: String,
1268 /// What was approved, which may differ from what was asked.
1269 #[serde(default)]
1270 pub decided_micros: Option<i64>,
1271 #[serde(default)]
1272 pub decided_by: Option<String>,
1273 /// The answer, as the owner sees it.
1274 #[serde(default)]
1275 pub answer: Option<String>,
1276 pub created_by: String,
1277 pub created_at: String,
1278 #[serde(default)]
1279 pub decided_at: Option<String>,
1280}
1281
1282/// `request_limit`: an owner asks g1t for more, or for help with usage past
1283/// what they meant. Answered within one business day, in the app and by
1284/// email. Owners only. Returns `Outcome<LimitRequest>`.
1285#[derive(Debug, Serialize, Deserialize)]
1286#[serde(rename_all = "camelCase")]
1287pub struct RequestLimitArgs {
1288 pub actor: User,
1289 pub workspace: String,
1290 /// `limit` or `overage`.
1291 pub kind: String,
1292 #[serde(alias = "amount_micros")]
1293 pub amount_micros: i64,
1294 pub reason: String,
1295 #[serde(default, alias = "expected_monthly_micros")]
1296 pub expected_monthly_micros: i64,
1297}
1298
1299/// `limit_requests`: a workspace's requests, newest first. Members only.
1300/// Returns `Outcome<Vec<LimitRequest>>`.
1301#[derive(Debug, Serialize, Deserialize)]
1302pub struct LimitRequestsArgs {
1303 pub workspace: String,
1304 pub viewer: Viewer,
1305}
1306
1307/// `confirm_spike`: an owner's answer to a spend spike. Keep going lifts the
1308/// pause for 24 hours, or until the hour's spend doubles again; stop keeps
1309/// new compute paused until an owner says to keep going. Owners only.
1310/// Returns `Outcome<Entitlements>`.
1311#[derive(Debug, Serialize, Deserialize)]
1312#[serde(rename_all = "camelCase")]
1313pub struct ConfirmSpikeArgs {
1314 pub actor: User,
1315 pub workspace: String,
1316 #[serde(alias = "keep_going")]
1317 pub keep_going: bool,
1318}
1319
1320/// `set_caps`: the owners' own caps on agents: one run's spend ($0.10 to
1321/// $100) and what the agents on one issue may spend in all ($1 to $1,000).
1322/// None goes back to the default ($2 and $10). A cap g1t staff set for the
1323/// account wins over both. Owners only. Returns `Outcome<Entitlements>`.
1324#[derive(Debug, Serialize, Deserialize)]
1325#[serde(rename_all = "camelCase")]
1326pub struct SetCapsArgs {
1327 pub actor: User,
1328 pub workspace: String,
1329 #[serde(default, alias = "run_cap_micros")]
1330 pub run_cap_micros: Option<i64>,
1331 #[serde(default, alias = "issue_cap_micros")]
1332 pub issue_cap_micros: Option<i64>,
1333}
1334
1335/// What staff see beside a request: the workspace's history with g1t.
1336#[derive(Clone, Debug, Default, Serialize, Deserialize)]
1337#[serde(rename_all = "camelCase")]
1338pub struct WorkspaceHistory {
1339 pub plan: Option<PlanKind>,
1340 /// The last six months, oldest first.
1341 pub months: Vec<MonthFigures>,
1342 /// Live payments that have cleared, and how many.
1343 pub paid_cleared_micros: i64,
1344 pub payments: u32,
1345 pub disputes: u32,
1346 pub declines: u32,
1347 /// The first time the workspace appears in billing, RFC 3339.
1348 pub first_seen: Option<String>,
1349 pub ceiling_micros: Option<i64>,
1350 pub max_ceiling_micros: Option<i64>,
1351 pub spend_limit_micros: Option<i64>,
1352 /// Recent velocity: the last hour, the usual hour over the last week,
1353 /// and the last 24 hours, at price.
1354 pub last_hour_micros: i64,
1355 pub average_hour_micros: i64,
1356 pub last_day_micros: i64,
1357}
1358
1359#[derive(Clone, Debug, Serialize, Deserialize)]
1360#[serde(rename_all = "camelCase")]
1361pub struct LimitRequestReview {
1362 pub request: LimitRequest,
1363 pub history: WorkspaceHistory,
1364}
1365
1366/// `admin_limit_requests`: requests for staff, oldest open first. Returns
1367/// `Vec<LimitRequestReview>`.
1368#[derive(Debug, Default, Serialize, Deserialize)]
1369pub struct AdminLimitRequestsArgs {
1370 /// `open` (the default), `approved`, `declined` or `all`.
1371 #[serde(default)]
1372 pub status: Option<String>,
1373}
1374
1375/// `admin_decide_limit_request`: approve (at the amount asked, or
1376/// `amount_micros`) or decline. The owner is told in the app and by email.
1377/// Recorded with who and why. Returns `Outcome<LimitRequest>`.
1378#[derive(Debug, Serialize, Deserialize)]
1379pub struct AdminDecideLimitRequestArgs {
1380 pub id: String,
1381 /// `approve` or `decline`.
1382 pub decision: String,
1383 #[serde(default)]
1384 pub amount_micros: Option<i64>,
1385 /// What the owner is told, beside the decision.
1386 #[serde(default)]
1387 pub note: String,
1388 pub by: String,
1389}
1390
1391/// `admin_record_payment`: money that reached g1t outside the card pages,
1392/// such as a bank transfer, entered as a payment (it raises the limit like
1393/// one). Recorded with who and the transfer's reference. Returns
1394/// `Outcome<LedgerEntry>`.
1395#[derive(Debug, Serialize, Deserialize)]
1396pub struct AdminRecordPaymentArgs {
1397 pub workspace: String,
1398 pub amount_micros: i64,
1399 /// The bank's reference for the transfer, or Stripe's payment id.
1400 pub reference: String,
1401 pub note: String,
1402 pub by: String,
1403}
1404
1405// --- Overages and goodwill (sudo) --------------------------------------------
1406
1407/// What a one-time goodwill credit would come to: g1t's margin on the
1408/// overage, always, plus as much of its underlying cost as the cap allows.
1409#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, Serialize, Deserialize)]
1410#[serde(rename_all = "camelCase")]
1411pub struct Goodwill {
1412 /// This month's charges above the workspace's typical month.
1413 pub overage_micros: i64,
1414 /// The part of the overage that is g1t's margin.
1415 pub margin_micros: i64,
1416 /// The part that is what g1t paid its providers.
1417 pub cost_micros: i64,
1418 /// The one-click credit: the margin plus the cost up to the cap.
1419 pub credit_micros: i64,
1420 /// Of the credit, the real cost g1t absorbs.
1421 pub absorbed_micros: i64,
1422}
1423
1424/// A workspace whose month went well past its usual, or hit a spike.
1425#[derive(Clone, Debug, Serialize, Deserialize)]
1426#[serde(rename_all = "camelCase")]
1427pub struct Overage {
1428 pub workspace: String,
1429 pub plan: PlanKind,
1430 /// The median of its last three months' charges.
1431 pub typical_month_micros: i64,
1432 pub this_month_micros: i64,
1433 /// What this month cost g1t, and what g1t keeps of it.
1434 pub cost_micros: i64,
1435 pub margin_micros: i64,
1436 /// A spike this month, if there was one.
1437 pub spike: Option<Spike>,
1438 /// The runs that cost the most this month.
1439 pub top_entries: Vec<LedgerEntry>,
1440 pub goodwill: Goodwill,
1441 /// False when a goodwill credit was given in the last 12 months.
1442 pub goodwill_available: bool,
1443 pub last_goodwill_at: Option<String>,
1444 /// An open overage request from the owner, if there is one.
1445 pub request: Option<LimitRequest>,
1446}
1447
1448/// `admin_overages`: the Overages queue. Returns `Vec<Overage>`.
1449#[derive(Debug, Default, Serialize, Deserialize)]
1450pub struct AdminOveragesArgs {}
1451
1452/// `admin_goodwill`: credits a workspace for accidental usage. With no
1453/// amount, the one-click credit (`Goodwill::credit_micros`), once per
1454/// workspace in 12 months. A larger amount, or a second within 12 months,
1455/// needs a typed reason. It shows on the statement as "Credit from g1t:
1456/// accidental usage on <date>". Returns `Outcome<LedgerEntry>`.
1457#[derive(Debug, Serialize, Deserialize)]
1458pub struct AdminGoodwillArgs {
1459 pub workspace: String,
1460 #[serde(default)]
1461 pub amount_micros: Option<i64>,
1462 /// Why, typed by staff; needed past the one-click credit.
1463 #[serde(default)]
1464 pub reason: String,
1465 /// The day the accidental usage happened, `YYYY-MM-DD`; today if absent.
1466 #[serde(default)]
1467 pub day: Option<String>,
1468 pub by: String,
1469}
1470
1471/// One workspace's recent pace, for sudo's velocity view.
1472#[derive(Clone, Debug, Serialize, Deserialize)]
1473#[serde(rename_all = "camelCase")]
1474pub struct Velocity {
1475 pub workspace: String,
1476 pub plan: PlanKind,
1477 pub last_hour_micros: i64,
1478 pub average_hour_micros: i64,
1479 pub last_day_micros: i64,
1480 pub this_month_micros: i64,
1481 /// The last hour over the usual hour; 0 with no history.
1482 pub ratio: f64,
1483 pub spike: Option<Spike>,
1484 pub first_seen: Option<String>,
1485}
1486
1487/// `admin_velocity`: workspaces spending in the last day, fastest first.
1488/// Returns `Vec<Velocity>`.
1489#[derive(Debug, Default, Serialize, Deserialize)]
1490pub struct AdminVelocityArgs {}
1491
1492#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize)]
1493#[serde(rename_all = "snake_case")]
1494pub enum AccountKind {
1495 Workspace,
1496 Enterprise,
1497}
1498
1499/// How an account is charged. Standard unless g1t set otherwise in sudo.
1500#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
1501#[serde(rename_all = "camelCase")]
1502pub struct Terms {
1503 pub kind: TermsKind,
1504 /// Off every usage charge, in percent. Custom terms only.
1505 #[serde(default)]
1506 pub discount_percent: u32,
1507 /// A ceiling on unpaid usage that replaces the one trust would give.
1508 #[serde(default)]
1509 pub ceiling_micros: Option<i64>,
1510 /// Why, for whoever looks next.
1511 #[serde(default)]
1512 pub note: String,
1513 /// When the terms end and the account goes back to standard.
1514 #[serde(default)]
1515 pub until: Option<String>,
1516 #[serde(default)]
1517 pub set_by: Option<String>,
1518 #[serde(default)]
1519 pub set_at: Option<String>,
1520}
1521
1522impl Terms {
1523 pub fn standard() -> Self {
1524 Terms {
1525 kind: TermsKind::Standard,
1526 discount_percent: 0,
1527 ceiling_micros: None,
1528 note: String::new(),
1529 until: None,
1530 set_by: None,
1531 set_at: None,
1532 }
1533 }
1534
1535 /// What a charge becomes under these terms.
1536 pub fn apply(&self, charge_micros: i64) -> i64 {
1537 match self.kind {
1538 TermsKind::Comped => 0,
1539 TermsKind::Custom => charge_micros * i64::from(100 - self.discount_percent.min(100)) / 100,
1540 TermsKind::Standard => charge_micros,
1541 }
1542 }
1543
1544 /// What a charge at cost plus the margin becomes under these terms, and
1545 /// how much of it g1t gives away by a discount: a sold charge is never
1546 /// below its cost plus the margin unless the difference is counted as
1547 /// given (`ledger.discount_micros`), never lost. Comped terms give it
1548 /// all, and are counted as comped elsewhere, so their given part is 0
1549 /// here.
1550 pub fn discounted(&self, charge_micros: i64) -> (i64, i64) {
1551 let charged = self.apply(charge_micros);
1552 match self.kind {
1553 TermsKind::Custom => (charged, (charge_micros - charged).max(0)),
1554 TermsKind::Comped | TermsKind::Standard => (charged, 0),
1555 }
1556 }
1557}
1558
1559#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize)]
1560#[serde(rename_all = "snake_case")]
1561pub enum TermsKind {
1562 /// Prices as published, limits by trust.
1563 Standard,
1564 /// Nothing charged; usage still recorded with its cost. Paid features
1565 /// are on without a plan. For g1t's own workspaces, partners, and the
1566 /// like.
1567 Comped,
1568 /// A discount, a ceiling, or both.
1569 Custom,
1570}
1571
1572/// `stripe_webhook`: an event from Stripe, as the API received it: the raw
1573/// body and its `Stripe-Signature` header. Billing checks the signature
1574/// against the secret of the endpoint it registered, and handles each
1575/// event once. Returns `Outcome<bool>`: false for one already handled.
1576#[derive(Debug, Serialize, Deserialize)]
1577pub struct StripeWebhookArgs {
1578 pub payload: String,
1579 pub signature: String,
1580}
1581
1582/// `admin_stripe`: where billing stands with Stripe. Staff only. Returns
1583/// `StripeStatus`. With `fix: true`, first enables the destination at
1584/// billing's address and gives it the events billing needs.
1585#[derive(Debug, Default, Serialize, Deserialize)]
1586pub struct AdminStripeArgs {
1587 #[serde(default)]
1588 pub fix: bool,
1589 #[serde(default)]
1590 pub by: Option<String>,
1591}
1592
1593#[derive(Clone, Debug, Serialize, Deserialize)]
1594#[serde(rename_all = "camelCase")]
1595pub struct StripeStatus {
1596 /// `test` or `live`, from the key; `off` without one.
1597 pub mode: String,
1598 /// Whether `STRIPE_WEBHOOK_SECRET` is set, so events can be checked.
1599 pub secret_set: bool,
1600 /// The destination at billing's address in Stripe, as Stripe has it.
1601 pub webhook: Option<StripeWebhook>,
1602 /// Events billing handles that the destination does not send.
1603 pub missing_events: Vec<String>,
1604 /// The latest events handled, newest first.
1605 pub recent_events: Vec<StripeEventSummary>,
1606 /// What went wrong reading or fixing the destination, if it did.
1607 pub error: Option<String>,
1608}
1609
1610#[derive(Clone, Debug, Serialize, Deserialize)]
1611#[serde(rename_all = "camelCase")]
1612pub struct StripeWebhook {
1613 pub url: String,
1614 pub endpoint_id: String,
1615 /// `enabled` or `disabled`.
1616 pub status: String,
1617 pub events: Vec<String>,
1618 pub created_at: String,
1619}
1620
1621#[derive(Clone, Debug, Serialize, Deserialize)]
1622#[serde(rename_all = "camelCase")]
1623pub struct StripeEventSummary {
1624 pub id: String,
1625 pub kind: String,
1626 pub outcome: String,
1627 pub received_at: String,
1628}
1629
1630/// `admin_enterprise_billing`: where an enterprise's invoices go. Creates
1631/// or updates its Stripe customer. Returns `Outcome<BillingAccount>`.
1632#[derive(Debug, Serialize, Deserialize)]
1633pub struct AdminEnterpriseBillingArgs {
1634 pub id: String,
1635 pub email: String,
1636 pub by: String,
1637}
1638
1639/// `admin_invoice_enterprise`: sends an enterprise its invoice now, for
1640/// what its workspaces owe, rather than waiting for the month to close.
1641/// Returns `Outcome<EnterpriseInvoice>`.
1642#[derive(Debug, Serialize, Deserialize)]
1643pub struct AdminInvoiceEnterpriseArgs {
1644 pub id: String,
1645 pub by: String,
1646}
1647
1648/// An enterprise's invoice: one line per workspace, paid on Stripe.
1649#[derive(Clone, Debug, Serialize, Deserialize)]
1650#[serde(rename_all = "camelCase")]
1651pub struct EnterpriseInvoice {
1652 pub invoice_id: String,
1653 /// Stripe's page for it, where it is paid.
1654 pub hosted_url: Option<String>,
1655 pub amount_micros: i64,
1656 /// `open`, `paid`, `overdue` or `void`.
1657 pub status: String,
1658 pub period: String,
1659 pub lines: Vec<InvoiceLine>,
1660 pub created_at: String,
1661}
1662
1663#[derive(Clone, Debug, Serialize, Deserialize)]
1664#[serde(rename_all = "camelCase")]
1665pub struct InvoiceLine {
1666 pub workspace: String,
1667 pub amount_micros: i64,
1668}
1669
1670/// A workspace's invoice from g1t: one per month, and one each time it is
1671/// charged near its limit. Itemised, charged to the card on file, and kept
1672/// in Stripe's billing page with its PDF.
1673#[derive(Clone, Debug, Serialize, Deserialize)]
1674#[serde(rename_all = "camelCase")]
1675pub struct WorkspaceInvoice {
1676 pub invoice_id: String,
1677 pub workspace: String,
1678 /// `month` (2026-10) or `threshold`.
1679 pub reason: String,
1680 pub period: String,
1681 pub amount_micros: i64,
1682 /// `paid`, `open`, `failed` or `void`.
1683 pub status: String,
1684 pub hosted_url: Option<String>,
1685 pub pdf_url: Option<String>,
1686 pub lines: Vec<InvoiceItem>,
1687 pub created_at: String,
1688}
1689
1690#[derive(Clone, Debug, Serialize, Deserialize)]
1691#[serde(rename_all = "camelCase")]
1692pub struct InvoiceItem {
1693 pub description: String,
1694 pub amount_micros: i64,
1695}
1696
1697/// `invoices`: a workspace's invoices from g1t, newest first. Members
1698/// only. Returns `Outcome<Vec<WorkspaceInvoice>>`.
1699#[derive(Debug, Serialize, Deserialize)]
1700pub struct InvoicesArgs {
1701 pub workspace: String,
1702 pub viewer: Viewer,
1703}
1704
1705/// `admin_workspace_invoices`: the same, for staff. Returns
1706/// `Vec<WorkspaceInvoice>`.
1707#[derive(Debug, Serialize, Deserialize)]
1708pub struct AdminWorkspaceInvoicesArgs {
1709 pub workspace: String,
1710}
1711
1712/// `statement`: a month of a workspace's ledger, grouped by day (or by
1713/// project) with a line per kind of charge. Members only. Returns
1714/// `Outcome<Statement>`.
1715#[derive(Debug, Serialize, Deserialize)]
1716pub struct StatementArgs {
1717 pub workspace: String,
1718 pub viewer: Viewer,
1719 /// YYYY-MM; this month when absent.
1720 #[serde(default)]
1721 pub month: Option<String>,
1722 /// `day` (the default) or `project`.
1723 #[serde(default)]
1724 pub group: Option<String>,
1725}
1726
1727#[derive(Clone, Debug, Serialize, Deserialize)]
1728#[serde(rename_all = "camelCase")]
1729pub struct Statement {
1730 pub month: String,
1731 /// Months with any entries, newest first.
1732 pub months: Vec<String>,
1733 pub groups: Vec<StatementGroup>,
1734 pub totals: StatementTotals,
1735}
1736
1737#[derive(Clone, Debug, Serialize, Deserialize)]
1738#[serde(rename_all = "camelCase")]
1739pub struct StatementGroup {
1740 /// The day (YYYY-MM-DD) or the project (`owner/name`, or empty).
1741 pub key: String,
1742 pub label: String,
1743 pub lines: Vec<StatementLine>,
1744 /// What the group's charges come to.
1745 pub charged_micros: i64,
1746}
1747
1748#[derive(Clone, Debug, Serialize, Deserialize)]
1749#[serde(rename_all = "camelCase")]
1750pub struct StatementLine {
1751 /// Agent runs, Sandbox time, Deployments, Payments, Credits from g1t,
1752 /// Refunds, and, for older entries, Runs on your own model provider.
1753 pub kind: String,
1754 pub count: u32,
1755 /// Charges positive; money in (payments, credits) negative.
1756 pub charged_micros: i64,
1757 pub cost_micros: i64,
1758 /// Of the usage on the line, what was paid for before it was charged:
1759 /// by the plan's included usage, the trial credit, g1t's open-source
1760 /// pool, or g1t itself. Not in `charged_micros`.
1761 #[serde(default)]
1762 pub covered_micros: i64,
1763}
1764
1765#[derive(Clone, Debug, Serialize, Deserialize)]
1766#[serde(rename_all = "camelCase")]
1767pub struct StatementTotals {
1768 pub charged_micros: i64,
1769 pub paid_micros: i64,
1770 pub cost_micros: i64,
1771 pub entries: u32,
1772 /// What paid for usage before it was charged, one line per source,
1773 /// such as "Paid by g1t's open-source pool".
1774 #[serde(default)]
1775 pub covered: Vec<Covered>,
1776 /// Owed when the month closed but under the minimum charge, so it
1777 /// carries over to the next invoice. Zero when nothing carried.
1778 #[serde(default)]
1779 pub carried_micros: i64,
1780}
1781
1782/// One source that paid for usage before it was charged.
1783#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
1784#[serde(rename_all = "camelCase")]
1785pub struct Covered {
1786 /// `included`, `trial`, `oss_pool` or `given`.
1787 pub source: String,
1788 /// "Paid by your plan's included usage", "Paid by your trial credit",
1789 /// "Paid by g1t's open-source pool", "Covered by g1t".
1790 pub label: String,
1791 pub micros: i64,
1792}
1793
1794/// `statement_entries`: one statement line's entries, newest first, 50 at
1795/// a time (`before` = the last id seen). Returns `Outcome<Vec<LedgerEntry>>`.
1796#[derive(Debug, Serialize, Deserialize)]
1797pub struct StatementEntriesArgs {
1798 pub workspace: String,
1799 pub viewer: Viewer,
1800 pub month: String,
1801 pub kind: String,
1802 #[serde(default)]
1803 pub day: Option<String>,
1804 #[serde(default)]
1805 pub project: Option<String>,
1806 #[serde(default)]
1807 pub before: Option<String>,
1808}
1809
1810// --- Sales (sudo.g1t.sh) ------------------------------------------------------
1811//
1812// What staff need to know to reach out: who is growing, who is close to
1813// their limit, who was declined, who has become a steady customer. And what
1814// was done about it: a stage, an owner on g1t's side, a next step, notes.
1815
1816/// Why a workspace is worth a look.
1817#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize)]
1818#[serde(rename_all = "snake_case")]
1819pub enum SignalKind {
1820 /// At its limit, or its own spend limit: work is stopped.
1821 AtLimit,
1822 /// Past 80% of what is available to it: about to need more.
1823 NearCeiling,
1824 /// Its card was declined or a payment disputed.
1825 Declined,
1826 /// This month is well ahead of last month.
1827 Growing,
1828 /// Became Established: the ceiling now follows its spend.
1829 Established,
1830 /// Paid g1t for the first time.
1831 FirstPayment,
1832 /// Spending enough that custom terms or an enterprise may suit it.
1833 HighSpend,
1834 /// Costs g1t more on Cloudflare than it pays, over 30 days: a pricing
1835 /// gap or abuse to look at (billing's `margin`).
1836 CostOverRevenue,
1837}
1838
1839#[derive(Clone, Debug, Serialize, Deserialize)]
1840#[serde(rename_all = "camelCase")]
1841pub struct Signal {
1842 pub workspace: String,
1843 pub kind: SignalKind,
1844 /// One sentence, with the figures.
1845 pub detail: String,
1846 /// The figure that matters, such as this month's spend.
1847 pub value_micros: i64,
1848 /// Its sales stage, if staff gave it one.
1849 pub stage: Option<String>,
1850 pub owner: Option<String>,
1851 #[serde(default)]
1852 pub next_step: Option<String>,
1853 /// When the next step is due, `YYYY-MM-DD`.
1854 #[serde(default)]
1855 pub next_at: Option<String>,
1856}
1857
1858/// `admin_invoices`: every invoice g1t has sent, workspaces' and
1859/// enterprises', newest first. Returns `Vec<InvoiceSummary>`.
1860#[derive(Debug, Default, Serialize, Deserialize)]
1861pub struct AdminInvoicesArgs {
1862 /// `paid`, `open`, `failed`, `overdue` or `void`.
1863 #[serde(default)]
1864 pub status: Option<String>,
1865 /// YYYY-MM, by when it was sent.
1866 #[serde(default)]
1867 pub month: Option<String>,
1868}
1869
1870#[derive(Clone, Debug, Serialize, Deserialize)]
1871#[serde(rename_all = "camelCase")]
1872pub struct InvoiceSummary {
1873 pub invoice_id: String,
1874 /// `workspace` or `enterprise`.
1875 pub kind: String,
1876 /// The workspace's slug, or the enterprise's account id.
1877 pub account: String,
1878 /// What to call it: the workspace, or the enterprise's name.
1879 pub name: String,
1880 pub reason: String,
1881 pub period: String,
1882 pub amount_micros: i64,
1883 pub status: String,
1884 pub hosted_url: Option<String>,
1885 pub created_at: String,
1886 pub paid_at: Option<String>,
1887}
1888
1889/// `admin_audit`: every change made in sudo, and by Stripe, newest first.
1890/// Returns `Vec<AdminAction>`.
1891#[derive(Debug, Default, Serialize, Deserialize)]
1892pub struct AdminAuditArgs {
1893 #[serde(default)]
1894 pub by: Option<String>,
1895 #[serde(default)]
1896 pub action: Option<String>,
1897 /// Only those before this time, for paging.
1898 #[serde(default)]
1899 pub before: Option<String>,
1900}
1901
1902/// `admin_signals`: every workspace worth reaching out to, most urgent
1903/// first. Returns `Vec<Signal>`.
1904#[derive(Debug, Default, Serialize, Deserialize)]
1905pub struct AdminSignalsArgs {}
1906
1907/// What staff are doing about a workspace.
1908#[derive(Clone, Debug, Serialize, Deserialize)]
1909#[serde(rename_all = "camelCase")]
1910pub struct SalesRecord {
1911 pub workspace: String,
1912 /// `none`, `lead`, `contacted`, `negotiating`, `won`, `lost` or `churn_risk`.
1913 pub stage: String,
1914 /// The staff member looking after it.
1915 pub owner: Option<String>,
1916 pub next_step: Option<String>,
1917 /// RFC 3339 date.
1918 pub next_at: Option<String>,
1919 pub notes: Vec<SalesNote>,
1920 pub updated_at: Option<String>,
1921}
1922
1923#[derive(Clone, Debug, Serialize, Deserialize)]
1924#[serde(rename_all = "camelCase")]
1925pub struct SalesNote {
1926 pub id: String,
1927 pub text: String,
1928 pub by: String,
1929 pub created_at: String,
1930}
1931
1932/// `admin_sales`: a workspace's sales record. Returns `SalesRecord`.
1933#[derive(Debug, Serialize, Deserialize)]
1934pub struct AdminSalesArgs {
1935 pub workspace: String,
1936}
1937
1938/// `admin_set_sales`: its stage, owner and next step. Returns `Outcome<SalesRecord>`.
1939#[derive(Debug, Serialize, Deserialize)]
1940pub struct AdminSetSalesArgs {
1941 pub workspace: String,
1942 pub stage: String,
1943 #[serde(default)]
1944 pub owner: Option<String>,
1945 #[serde(default)]
1946 pub next_step: Option<String>,
1947 #[serde(default)]
1948 pub next_at: Option<String>,
1949 pub by: String,
1950}
1951
1952/// `admin_add_note`. Returns `Outcome<SalesRecord>`.
1953#[derive(Debug, Serialize, Deserialize)]
1954pub struct AdminAddNoteArgs {
1955 pub workspace: String,
1956 pub text: String,
1957 pub by: String,
1958}
1959
1960/// `admin_overview`: the business at a glance. Returns `Overview`.
1961#[derive(Debug, Default, Serialize, Deserialize)]
1962pub struct AdminOverviewArgs {}
1963
1964#[derive(Clone, Debug, Serialize, Deserialize)]
1965#[serde(rename_all = "camelCase")]
1966pub struct Overview {
1967 /// YYYY-MM.
1968 pub month: String,
1969 /// The last six months, oldest first, all workspaces together.
1970 pub months: Vec<MonthFigures>,
1971 /// This month by kind of usage: models, sandbox, deployments, plans.
1972 pub by_kind: Vec<KindFigures>,
1973 pub paying_workspaces: u32,
1974 pub stopped: u32,
1975 pub near_ceiling: u32,
1976 pub declined: u32,
1977 /// Sent and not yet paid, workspaces and enterprises.
1978 pub open_invoices_micros: i64,
1979 /// Follow-ups due today or earlier.
1980 pub follow_ups_due: u32,
1981 /// The capped budgets g1t pays from, this month.
1982 #[serde(default)]
1983 pub pools: Option<Pools>,
1984 /// This month's revenue: usage charged plus the plan's price paid.
1985 #[serde(default)]
1986 pub revenue_micros: i64,
1987 /// Workspaces on the paid plan now, and what their price comes to a
1988 /// month.
1989 #[serde(default)]
1990 pub active_plans: u32,
1991 #[serde(default)]
1992 pub plan_mrr_micros: i64,
1993 /// What g1t gave this month, by source, apart from its margin.
1994 #[serde(default)]
1995 pub given: Vec<GivenFigures>,
1996 /// g1t's own and Flagon's workspaces this month: what their use cost,
1997 /// and why they are not charged.
1998 #[serde(default)]
1999 pub internal: Vec<InternalUse>,
2000 /// Open limit requests, and workspaces in the Overages queue.
2001 #[serde(default)]
2002 pub open_requests: u32,
2003 #[serde(default)]
2004 pub overages: u32,
2005 /// Spend spikes waiting for an owner.
2006 #[serde(default)]
2007 pub open_spikes: u32,
2008}
2009
2010/// What g1t gave this month from one source.
2011#[derive(Clone, Debug, Default, PartialEq, Serialize, Deserialize)]
2012#[serde(rename_all = "camelCase")]
2013pub struct GivenFigures {
2014 /// `internal`, `trial`, `oss_pool`, `goodwill` or `covered`.
2015 pub source: String,
2016 pub label: String,
2017 /// At price, and what it cost g1t.
2018 pub micros: i64,
2019 pub cost_micros: i64,
2020}
2021
2022/// One internal workspace's use this month.
2023#[derive(Clone, Debug, Default, PartialEq, Serialize, Deserialize)]
2024#[serde(rename_all = "camelCase")]
2025pub struct InternalUse {
2026 pub workspace: String,
2027 /// Why it is not charged: its terms' note.
2028 pub reason: String,
2029 pub cost_micros: i64,
2030 pub entries: u32,
2031}
2032
2033/// g1t's capped budgets for free usage, this calendar month (UTC).
2034#[derive(Clone, Debug, Default, Serialize, Deserialize)]
2035#[serde(rename_all = "camelCase")]
2036pub struct Pools {
2037 /// YYYY-MM.
2038 pub month: String,
2039 /// Trial grants made this month, against the month's pool.
2040 pub trial_granted_micros: i64,
2041 pub trial_pool_micros: i64,
2042 pub trial_grants: u32,
2043 /// What the open-source pool paid this month, against its cap.
2044 pub oss_used_micros: i64,
2045 pub oss_pool_micros: i64,
2046 /// Each public repository's monthly cap on the pool.
2047 pub oss_repo_micros: i64,
2048}
2049
2050#[derive(Clone, Debug, Serialize, Deserialize)]
2051#[serde(rename_all = "camelCase")]
2052pub struct KindFigures {
2053 pub kind: String,
2054 pub charged_micros: i64,
2055 pub cost_micros: i64,
2056}
2057
2058// --- Staff (sudo.g1t.sh) ------------------------------------------------------
2059//
2060// Called only by the sudo app, which only g1t staff can reach (behind
2061// Cloudflare Access). Each change names who made it, and is kept in the
2062// audit log.
2063
2064/// `admin_accounts`: every billing account, with where each stands this
2065/// month. Returns `Vec<AccountSummary>`.
2066#[derive(Debug, Default, Serialize, Deserialize)]
2067pub struct AdminAccountsArgs {
2068 #[serde(default)]
2069 pub query: Option<String>,
2070 /// Exactly these workspaces' accounts, such as one page of sudo's
2071 /// list; every account with activity when absent.
2072 #[serde(default)]
2073 pub workspaces: Option<Vec<String>>,
2074}
2075
2076#[derive(Clone, Debug, Serialize, Deserialize)]
2077#[serde(rename_all = "camelCase")]
2078pub struct AccountSummary {
2079 pub account: BillingAccount,
2080 pub limit: Limit,
2081 /// Charged this month, after terms.
2082 pub charged_micros: i64,
2083 /// What this month's usage cost g1t.
2084 pub cost_micros: i64,
2085 /// Paid, ever.
2086 pub paid_micros: i64,
2087 /// The same figures for each of the account's workspaces that has
2088 /// any, so staff can see what one member of an enterprise used.
2089 #[serde(default)]
2090 pub by_workspace: Vec<WorkspaceFigures>,
2091 /// The last six months, oldest first, for trends.
2092 #[serde(default)]
2093 pub months: Vec<MonthFigures>,
2094}
2095
2096/// One month of an account's billing.
2097#[derive(Clone, Debug, Default, PartialEq, Serialize, Deserialize)]
2098#[serde(rename_all = "camelCase")]
2099pub struct MonthFigures {
2100 /// YYYY-MM.
2101 pub month: String,
2102 /// Usage charged, after what paid for it first.
2103 pub charged_micros: i64,
2104 /// What usage cost g1t: only what g1t paid for, never a workspace's own
2105 /// model provider.
2106 pub cost_micros: i64,
2107 pub paid_micros: i64,
2108 /// The plan's monthly price, paid.
2109 #[serde(default)]
2110 pub plans_micros: i64,
2111 /// What g1t gave, at price: internal (comped) use, trials, the
2112 /// open-source pool, goodwill credits and what g1t covered. Not margin.
2113 #[serde(default)]
2114 pub given_micros: i64,
2115}
2116
2117/// One workspace's share of an [`AccountSummary`].
2118#[derive(Clone, Debug, Default, PartialEq, Serialize, Deserialize)]
2119#[serde(rename_all = "camelCase")]
2120pub struct WorkspaceFigures {
2121 pub workspace: String,
2122 pub charged_micros: i64,
2123 pub cost_micros: i64,
2124 pub paid_micros: i64,
2125}
2126
2127/// `admin_account`: one account in full. Returns `Outcome<AccountDetail>`.
2128#[derive(Debug, Serialize, Deserialize)]
2129pub struct AdminAccountArgs {
2130 /// An account id, or a workspace slug.
2131 pub id: String,
2132}
2133
2134#[derive(Clone, Debug, Serialize, Deserialize)]
2135#[serde(rename_all = "camelCase")]
2136pub struct AccountDetail {
2137 pub summary: AccountSummary,
2138 /// Each workspace's limit, for an enterprise.
2139 pub workspaces: Vec<Limit>,
2140 pub ledger: Vec<LedgerEntry>,
2141 pub audit: Vec<AdminAction>,
2142}
2143
2144/// `admin_set_terms`. Returns `Outcome<BillingAccount>`.
2145#[derive(Debug, Serialize, Deserialize)]
2146pub struct AdminSetTermsArgs {
2147 pub id: String,
2148 pub terms: Terms,
2149 pub by: String,
2150}
2151
2152/// `admin_create_enterprise`. Returns `Outcome<BillingAccount>`.
2153#[derive(Debug, Serialize, Deserialize)]
2154pub struct AdminCreateEnterpriseArgs {
2155 pub name: String,
2156 pub workspaces: Vec<String>,
2157 pub by: String,
2158}
2159
2160/// `admin_attach`: moves a workspace onto an enterprise account, or back
2161/// onto its own with `account: None`. Returns `Outcome<BillingAccount>`.
2162#[derive(Debug, Serialize, Deserialize)]
2163pub struct AdminAttachArgs {
2164 pub workspace: String,
2165 pub account: Option<String>,
2166 pub by: String,
2167}
2168
2169/// `admin_credit`: money g1t gives a workspace, such as a refund or a
2170/// goodwill credit. Returns `Outcome<LedgerEntry>`.
2171#[derive(Debug, Serialize, Deserialize)]
2172pub struct AdminCreditArgs {
2173 pub workspace: String,
2174 pub amount_micros: i64,
2175 pub note: String,
2176 pub by: String,
2177}
2178
2179/// `admin_reset_billing`: a test workspace's billing wiped, so it starts
2180/// again as a new customer. Only while billing runs on Stripe's test key;
2181/// never a comped workspace or one an enterprise pays for. `confirm` is the
2182/// workspace's slug typed out. Returns `Outcome<BillingReset>`.
2183#[derive(Debug, Serialize, Deserialize)]
2184pub struct AdminResetBillingArgs {
2185 pub workspace: String,
2186 pub confirm: String,
2187 pub note: String,
2188 pub by: String,
2189}
2190
2191/// What a reset removed.
2192#[derive(Clone, Debug, Serialize, Deserialize)]
2193#[serde(rename_all = "camelCase")]
2194pub struct BillingReset {
2195 pub workspace: String,
2196 pub rows: u32,
2197 /// Whether the costs analysis ran again after it, so the margin
2198 /// figures no longer hold the workspace's past usage.
2199 #[serde(default)]
2200 pub refreshed: bool,
2201}
2202
2203/// One change made in sudo.
2204#[derive(Clone, Debug, Serialize, Deserialize)]
2205#[serde(rename_all = "camelCase")]
2206pub struct AdminAction {
2207 pub id: String,
2208 pub account: String,
2209 pub action: String,
2210 pub detail: String,
2211 pub by: String,
2212 pub created_at: String,
2213}
2214
2215/// What a feature's plan costs and includes.
2216#[derive(Clone, Debug, Serialize, Deserialize)]
2217#[serde(rename_all = "camelCase")]
2218pub struct Plan {
2219 pub feature: Feature,
2220 pub title: String,
2221 /// Charged every month while the plan is on, in cents.
2222 pub monthly_cents: u32,
2223 /// What the monthly price includes, one line each, for people to read.
2224 pub includes: Vec<String>,
2225 /// How usage past the allowance is charged, for people to read.
2226 pub overage: String,
2227}
2228
2229#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize)]
2230#[serde(rename_all = "snake_case")]
2231pub enum SubscriptionStatus {
2232 /// Paid up; the feature works.
2233 Active,
2234 /// Paid up to the end of the period, and ends then.
2235 Canceling,
2236 /// The last payment failed; the feature is off until it is paid.
2237 PastDue,
2238 /// Ended.
2239 Canceled,
2240}
2241
2242impl SubscriptionStatus {
2243 /// Whether the feature works in this state.
2244 pub fn on(self) -> bool {
2245 matches!(self, SubscriptionStatus::Active | SubscriptionStatus::Canceling)
2246 }
2247}
2248
2249/// A workspace's plan for one feature.
2250#[derive(Clone, Debug, Serialize, Deserialize)]
2251#[serde(rename_all = "camelCase")]
2252pub struct Subscription {
2253 pub feature: Feature,
2254 pub status: SubscriptionStatus,
2255 /// RFC 3339: when the period paid for ends, and the plan renews or
2256 /// ends.
2257 pub period_end: Option<String>,
2258 /// Username of whoever turned it on.
2259 pub started_by: String,
2260 /// RFC 3339.
2261 pub started_at: String,
2262}
2263
2264/// A feature as a workspace sees it: what it costs, and its plan if it has
2265/// one.
2266#[derive(Clone, Debug, Serialize, Deserialize)]
2267#[serde(rename_all = "camelCase")]
2268pub struct FeatureState {
2269 pub plan: Plan,
2270 pub subscription: Option<Subscription>,
2271 /// Whether the feature works for the workspace now.
2272 pub on: bool,
2273 /// On without a plan: comped terms, or given by g1t. Nothing to pay
2274 /// and nothing to turn off.
2275 #[serde(default)]
2276 pub included: bool,
2277}
2278
2279/// `features`: every paid feature and the workspace's plan for each.
2280/// Members only. Returns `Outcome<Vec<FeatureState>>`.
2281#[derive(Debug, Serialize, Deserialize)]
2282pub struct FeaturesArgs {
2283 pub workspace: String,
2284 pub viewer: Viewer,
2285}
2286
2287/// `subscribe`: starts the card page for a feature's monthly plan. Owners
2288/// only. Returns `Outcome<Checkout>`; the page's id comes back to
2289/// `return_url` as `session`, for `confirm_subscription`.
2290#[derive(Debug, Serialize, Deserialize)]
2291#[serde(rename_all = "camelCase")]
2292pub struct SubscribeArgs {
2293 pub actor: User,
2294 pub workspace: String,
2295 pub feature: Feature,
2296 pub return_url: String,
2297}
2298
2299/// `confirm_subscription`: turns the feature on once the processor says
2300/// the plan was paid for. Safe to call any number of times. Returns
2301/// `Outcome<FeatureState>`.
2302#[derive(Debug, Serialize, Deserialize)]
2303pub struct ConfirmSubscriptionArgs {
2304 pub workspace: String,
2305 pub viewer: Viewer,
2306 pub session: String,
2307}
2308
2309/// `admin_log`: a staff change another service made to a workspace, kept
2310/// in sudo's audit log with billing's own (`admin_audit`). For identity's
2311/// restores and purges of deleted workspaces. Returns `bool`.
2312#[derive(Debug, Serialize, Deserialize)]
2313pub struct AdminLogArgs {
2314 pub workspace: String,
2315 pub action: String,
2316 pub detail: String,
2317 /// The staff member's email.
2318 pub by: String,
2319}
2320
2321/// `close_workspace`: settles a workspace that is about to be deleted.
2322/// Owners only. Refused while it has an invoice that failed, while it
2323/// holds prepaid credit, or while it owes money it cannot be charged for
2324/// now; otherwise what it owes is invoiced to its card at once (no
2325/// minimum), its plan is cancelled at Stripe straight away, and its
2326/// account is marked closed, so the month-end close, autopay and limit
2327/// warnings pass it by. Its ledger, invoices and statements stay. With
2328/// `dry_run`, only says whether it could, changing nothing. Returns
2329/// `Outcome<bool>`.
2330#[derive(Debug, Serialize, Deserialize)]
2331#[serde(rename_all = "camelCase")]
2332pub struct CloseWorkspaceArgs {
2333 pub actor: User,
2334 pub workspace: String,
2335 #[serde(default)]
2336 pub dry_run: bool,
2337}
2338
2339/// `cancel_subscription` (`resume` false) ends a plan at the end of the
2340/// period paid for; with `resume` true, takes that back. Owners only.
2341/// Returns `Outcome<FeatureState>`.
2342#[derive(Debug, Serialize, Deserialize)]
2343pub struct CancelSubscriptionArgs {
2344 pub actor: User,
2345 pub workspace: String,
2346 pub feature: Feature,
2347 #[serde(default)]
2348 pub resume: bool,
2349}
2350
2351/// `has_feature`: whether a feature works for a workspace now, asked by the
2352/// service that provides it before doing paid work. Returns
2353/// `Outcome<bool>`: a failure, with the reason to show, when it does not.
2354/// True everywhere when no card processor is configured.
2355#[derive(Debug, Serialize, Deserialize)]
2356pub struct HasFeatureArgs {
2357 pub workspace: String,
2358 pub feature: Feature,
2359}
2360
2361/// `charge_feature`: usage of a feature past its plan's allowance, charged
2362/// from the workspace's credit at cost plus the margin, whatever
2363/// `FREE_WHILE_BUILDING` says. Called by the service that provides it.
2364/// Charged once per `reference`. Returns `Outcome<bool>`: false if that
2365/// reference was charged before.
2366#[derive(Debug, Serialize, Deserialize)]
2367#[serde(rename_all = "camelCase")]
2368pub struct ChargeFeatureArgs {
2369 pub workspace: String,
2370 pub feature: Feature,
2371 /// What it cost g1t, in millionths of a dollar, before the margin.
2372 pub cost_micros: i64,
2373 pub description: String,
2374 /// `namespace/name`, when the usage was one repository's.
2375 pub repo: Option<String>,
2376 /// Unique to this charge, e.g. `deployments/acme/2026-10`.
2377 pub reference: String,
2378 /// For a build: how long it ran. The plan's included build time this
2379 /// month pays for what it can, and only the rest of `cost_micros` is
2380 /// charged.
2381 #[serde(default)]
2382 pub build_seconds: Option<u32>,
2383}
2384
2385// ---------------------------------------------------------------------
2386// Costs and margin: what Cloudflare charges g1t against what g1t
2387// charges (billing's costs.rs, margin.rs and pricing.rs). Staff only.
2388// ---------------------------------------------------------------------
2389
2390/// `admin_costs`: the Costs & margin page. Returns `CostsReport`.
2391#[derive(Debug, Default, Serialize, Deserialize)]
2392pub struct AdminCostsArgs {
2393 /// How many days back, 7 to 90; 30 when absent.
2394 #[serde(default)]
2395 pub days: Option<u32>,
2396}
2397
2398/// One of g1t's products on one day.
2399#[derive(Clone, Debug, Default, Serialize, Deserialize)]
2400#[serde(rename_all = "camelCase")]
2401pub struct CostDay {
2402 pub day: String,
2403 pub bucket: String,
2404 /// What Cloudflare charged g1t.
2405 pub cf_cost_micros: i64,
2406 /// What g1t's meters recorded it cost, at the price book's cost.
2407 pub own_cost_micros: i64,
2408 /// What customers were charged for it at price, before included
2409 /// usage, trials and pools paid for some.
2410 pub value_micros: i64,
2411 /// Of that, what workspaces paid.
2412 pub cash_micros: i64,
2413}
2414
2415/// One product over the range.
2416#[derive(Clone, Debug, Default, Serialize, Deserialize)]
2417#[serde(rename_all = "camelCase")]
2418pub struct ProductMargin {
2419 pub bucket: String,
2420 pub title: String,
2421 /// The cost the margin is taken from: Cloudflare's bill, or g1t's own
2422 /// figure for what Cloudflare does not bill (models).
2423 pub cost_micros: i64,
2424 pub cf_cost_micros: i64,
2425 pub own_cost_micros: i64,
2426 pub value_micros: i64,
2427 pub margin_micros: i64,
2428 pub margin_percent: Option<f64>,
2429 /// `cloudflare` or `ledger`.
2430 pub cost_source: String,
2431 /// Running g1t itself, paid for by the plan.
2432 pub overhead: bool,
2433}
2434
2435/// All of g1t over the range: money in against every cost, and against
2436/// the cost of what was sold (every cost less what g1t gave away).
2437#[derive(Clone, Debug, Default, Serialize, Deserialize)]
2438#[serde(rename_all = "camelCase")]
2439pub struct OverallMargin {
2440 /// What workspaces paid for usage, and for the plan.
2441 pub usage_micros: i64,
2442 pub plans_micros: i64,
2443 pub cost_micros: i64,
2444 pub margin_micros: i64,
2445 pub margin_percent: Option<f64>,
2446 /// Of `cost_micros`, what went on usage g1t gave away on purpose:
2447 /// comped workspaces, free periods, the trial and the open-source pool.
2448 #[serde(default)]
2449 pub given_micros: i64,
2450 /// Money in against `cost_micros - given_micros`.
2451 #[serde(default)]
2452 pub sold_margin_micros: i64,
2453 #[serde(default)]
2454 pub sold_margin_percent: Option<f64>,
2455 /// What was sold, apart: usage (`usage_micros` against what that usage
2456 /// cost, less what was given), running g1t (`plans_micros` against the
2457 /// platform's cost, less its given share) and what no mapping names.
2458 #[serde(default)]
2459 pub usage_cost_micros: i64,
2460 #[serde(default)]
2461 pub usage_margin_micros: i64,
2462 #[serde(default)]
2463 pub usage_margin_percent: Option<f64>,
2464 #[serde(default)]
2465 pub running_cost_micros: i64,
2466 #[serde(default)]
2467 pub unmapped_cost_micros: i64,
2468 /// `given_micros` by why: comped workspaces, free use (free periods,
2469 /// free allowances, overruns g1t covered), the trial, the open-source pool.
2470 #[serde(default)]
2471 pub given_comped_micros: i64,
2472 #[serde(default)]
2473 pub given_free_micros: i64,
2474 #[serde(default)]
2475 pub given_trial_micros: i64,
2476 #[serde(default)]
2477 pub given_pool_micros: i64,
2478 /// What discounts on an account's terms took below cost plus the
2479 /// margin: given, so a discounted sale is not margin lost.
2480 #[serde(default)]
2481 pub given_discount_micros: i64,
2482 /// `cost_micros` by who g1t pays: Cloudflare's bill (billed amounts,
2483 /// after the included allowances), and model providers (the ledger's
2484 /// cost of the tokens, which Cloudflare's bill does not show).
2485 /// What the plan's included usage paid for, at price (the ledger's
2486 /// `credit_micros`, comped workspaces left out): money in for usage,
2487 /// paid out of `plans_micros`.
2488 #[serde(default)]
2489 pub included_micros: i64,
2490 #[serde(default)]
2491 pub cloudflare_cost_micros: i64,
2492 #[serde(default)]
2493 pub models_cost_micros: i64,
2494}
2495
2496/// A count, cost or leak that does not add up.
2497#[derive(Clone, Debug, Serialize, Deserialize)]
2498#[serde(rename_all = "camelCase")]
2499pub struct CostDrift {
2500 pub bucket: String,
2501 pub title: String,
2502 /// `count` (units g1t counted against Cloudflare's), `cost` (the bill
2503 /// against the price book's cost of the same usage; for models, what AI
2504 /// Gateway priced g1t's provider traffic at against the ledger's model
2505 /// cost), `unpriced` (model usage AI Gateway put no price on, so its
2506 /// cost is not the providers'), or `leak`.
2507 pub kind: String,
2508 pub ours: f64,
2509 pub cloudflare: f64,
2510 pub delta_percent: Option<f64>,
2511 pub detail: String,
2512 pub found_at: String,
2513}
2514
2515/// A margin alert, open while its condition lasts.
2516#[derive(Clone, Debug, Serialize, Deserialize)]
2517#[serde(rename_all = "camelCase")]
2518pub struct MarginAlert {
2519 pub id: String,
2520 /// `margin`, `overall`, `leak`, `drift` or `workspace`.
2521 pub kind: String,
2522 /// The product, or the workspace.
2523 pub subject: String,
2524 pub detail: String,
2525 pub since: String,
2526 pub opened_at: String,
2527 pub emailed_at: Option<String>,
2528}
2529
2530/// A change to a price the reconciler measured.
2531#[derive(Clone, Debug, Serialize, Deserialize)]
2532#[serde(rename_all = "camelCase")]
2533pub struct PriceProposal {
2534 pub id: String,
2535 pub meter: String,
2536 pub title: String,
2537 pub unit: String,
2538 pub current_cost_micros: f64,
2539 pub proposed_cost_micros: f64,
2540 pub change_percent: f64,
2541 pub markup_percent: u32,
2542 pub reason: String,
2543 /// `keeper` or `reconciler`.
2544 pub source: String,
2545 /// Far off the current cost: look before approving.
2546 pub suspect: bool,
2547 /// `open`, `applied`, `approved`, `rejected` or `superseded`.
2548 pub status: String,
2549 pub created_at: String,
2550 pub decided_at: Option<String>,
2551 pub decided_by: Option<String>,
2552 pub note: Option<String>,
2553 /// When it takes or took effect, once approved or applied.
2554 pub effective_at: Option<String>,
2555}
2556
2557/// One version of one meter's price. Never changed once written.
2558#[derive(Clone, Debug, Serialize, Deserialize)]
2559#[serde(rename_all = "camelCase")]
2560pub struct PriceVersion {
2561 pub id: String,
2562 pub meter: String,
2563 pub version: u32,
2564 pub cost_micros: f64,
2565 pub markup_percent: u32,
2566 pub price_micros: f64,
2567 pub effective_at: String,
2568 pub reason: String,
2569 pub created_by: String,
2570 /// When the price book took it on; absent while it waits for its date.
2571 pub applied_at: Option<String>,
2572}
2573
2574/// What a workspace cost g1t over the range, Cloudflare's costs shared
2575/// out by g1t's own meters, against what it paid.
2576#[derive(Clone, Debug, Serialize, Deserialize)]
2577#[serde(rename_all = "camelCase")]
2578pub struct WorkspaceCost {
2579 pub workspace: String,
2580 pub cost_micros: i64,
2581 pub revenue_micros: i64,
2582 /// Of `cost_micros`, what g1t gave away.
2583 #[serde(default)]
2584 pub given_micros: i64,
2585 /// One of g1t's own (comped) workspaces.
2586 pub internal: bool,
2587}
2588
2589/// One Cloudflare meter over the range, and the product it is a cost of.
2590#[derive(Clone, Debug, Serialize, Deserialize)]
2591#[serde(rename_all = "camelCase")]
2592pub struct CostLineSummary {
2593 pub product: String,
2594 pub meter: String,
2595 pub raw_name: String,
2596 pub unit: String,
2597 pub source: String,
2598 pub quantity: f64,
2599 pub cost_micros: i64,
2600 /// Absent when no mapping claims it.
2601 pub bucket: Option<String>,
2602}
2603
2604/// A row of the mapping from Cloudflare's meters to g1t's products.
2605#[derive(Clone, Debug, Serialize, Deserialize)]
2606#[serde(rename_all = "camelCase")]
2607pub struct CostMapping {
2608 pub product: String,
2609 pub meter: String,
2610 pub bucket: String,
2611 pub price_meter: Option<String>,
2612 pub own_meter: Option<String>,
2613 pub scale_to_own: bool,
2614 pub drift_percent: f64,
2615 pub note: String,
2616 pub updated_at: String,
2617 pub updated_by: String,
2618}
2619
2620/// The guardrails on prices and the alerts.
2621#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
2622#[serde(rename_all = "camelCase")]
2623pub struct CostSettings {
2624 /// Apply small moves without staff.
2625 pub auto_apply: bool,
2626 /// The largest move applied without staff, either way, in percent.
2627 pub auto_apply_percent: f64,
2628 /// Days between telling customers of a rise and charging it.
2629 pub notice_days: u32,
2630 /// Below this margin, in percent, for `alert_days` days in a row, alert.
2631 pub margin_floor_percent: f64,
2632 pub alert_days: u32,
2633 /// Days with less cost than this say nothing about a margin.
2634 pub min_daily_cost_micros: i64,
2635 /// A workspace costing more than its revenue times this, over 30 days,
2636 /// and at least `anomaly_floor_micros`, is flagged.
2637 pub anomaly_factor: f64,
2638 pub anomaly_floor_micros: i64,
2639}
2640
2641impl Default for CostSettings {
2642 fn default() -> Self {
2643 CostSettings {
2644 auto_apply: true,
2645 auto_apply_percent: 25.0,
2646 notice_days: 14,
2647 margin_floor_percent: 10.0,
2648 alert_days: 3,
2649 min_daily_cost_micros: 100_000,
2650 anomaly_factor: 1.0,
2651 anomaly_floor_micros: 1_000_000,
2652 }
2653 }
2654}
2655
2656/// The Costs & margin page.
2657#[derive(Clone, Debug, Default, Serialize, Deserialize)]
2658#[serde(rename_all = "camelCase")]
2659pub struct CostsReport {
2660 /// A token to read Cloudflare's bill is set.
2661 pub configured: bool,
2662 /// When Cloudflare's bill was last read.
2663 pub fetched_at: Option<String>,
2664 /// The days shown, YYYY-MM-DD.
2665 pub since: String,
2666 pub until: String,
2667 pub days: Vec<CostDay>,
2668 pub products: Vec<ProductMargin>,
2669 pub overall: OverallMargin,
2670 pub drift: Vec<CostDrift>,
2671 pub alerts: Vec<MarginAlert>,
2672 pub proposals: Vec<PriceProposal>,
2673 pub versions: Vec<PriceVersion>,
2674 pub top_workspaces: Vec<WorkspaceCost>,
2675 pub lines: Vec<CostLineSummary>,
2676 pub mappings: Vec<CostMapping>,
2677 pub settings: CostSettings,
2678 /// g1t's own spend against its two caps.
2679 #[serde(default)]
2680 pub caps: SpendCaps,
2681}
2682
2683/// What g1t itself pays for, against its caps (billing's `budget`): the
2684/// daily breaker on all of it, and each comped account's monthly budget.
2685/// One of Cloudflare's subscriptions, at what it comes to a month.
2686#[derive(Clone, Debug, Default, PartialEq, Serialize, Deserialize)]
2687#[serde(rename_all = "camelCase")]
2688pub struct FixedCost {
2689 pub name: String,
2690 pub monthly_micros: i64,
2691}
2692
2693/// At cost, never at price. What sudo's Costs page and its red bar show.
2694#[derive(Clone, Debug, Default, Serialize, Deserialize)]
2695#[serde(rename_all = "camelCase")]
2696pub struct SpendCaps {
2697 /// Today (UTC), YYYY-MM-DD, and this month, YYYY-MM.
2698 pub day: String,
2699 pub month: String,
2700 /// What g1t paid for itself today across every workspace: comped work,
2701 /// the trial and open-source pools, free workspaces' overruns, and
2702 /// anything charged without real money behind it.
2703 pub today_micros: i64,
2704 /// `PLATFORM_DAILY_SPEND_CAP_MICROS`. Zero: no breaker.
2705 pub daily_cap_micros: i64,
2706 /// The breaker is open: new hosted-model agent runs that g1t would pay
2707 /// for wait until tomorrow (UTC) or until staff lift it.
2708 pub tripped: bool,
2709 pub tripped_at: Option<String>,
2710 /// Staff lifted it for the rest of the day.
2711 pub lifted_by: Option<String>,
2712 pub lifted_at: Option<String>,
2713 pub lift_note: Option<String>,
2714 /// This month so far, by what paid: `comped`, `trial`, `oss`, `given`,
2715 /// `unpaid`.
2716 pub month_buckets: Vec<SpendBucket>,
2717 /// Each comped account's monthly budget.
2718 pub comped: Vec<CompedBudget>,
2719 /// Free workspaces' share of this month's reconciled costs (git,
2720 /// storage, platform), through yesterday.
2721 pub free_tier_micros: i64,
2722 /// Cloudflare's subscriptions a month: as read from Cloudflare each
2723 /// day, else `CLOUDFLARE_FIXED_MONTHLY_MICROS`, an estimate.
2724 pub fixed_monthly_micros: i64,
2725 /// `cloudflare` or `estimate`.
2726 #[serde(default)]
2727 pub fixed_source: String,
2728 #[serde(default)]
2729 pub fixed_read_at: Option<String>,
2730 /// Each subscription, when read from Cloudflare.
2731 #[serde(default)]
2732 pub fixed_items: Vec<FixedCost>,
2733 /// Money in this month, through the last reconciled day.
2734 pub revenue_micros: i64,
2735}
2736
2737#[derive(Clone, Debug, Default, Serialize, Deserialize)]
2738#[serde(rename_all = "camelCase")]
2739pub struct SpendBucket {
2740 pub bucket: String,
2741 pub title: String,
2742 pub micros: i64,
2743}
2744
2745/// A comped account's monthly budget: what its work cost g1t this month.
2746#[derive(Clone, Debug, Default, Serialize, Deserialize)]
2747#[serde(rename_all = "camelCase")]
2748pub struct CompedBudget {
2749 pub account: String,
2750 pub name: String,
2751 pub used_micros: i64,
2752 /// Zero: no budget.
2753 pub ceiling_micros: i64,
2754 /// The ceiling is `COMPED_MONTHLY_CEILING_MICROS`, not the account's own.
2755 pub default_ceiling: bool,
2756 /// 50, 75, 90, 100, or 0.
2757 pub level: u32,
2758}
2759
2760/// `admin_spend_caps`: g1t's own spend against its caps. Returns `SpendCaps`.
2761#[derive(Debug, Default, Serialize, Deserialize)]
2762pub struct AdminSpendCapsArgs {}
2763
2764/// `admin_lift_breaker`: lets hosted-model runs start again for the rest
2765/// of today (UTC), with why. Recorded in the audit log. Returns
2766/// `Outcome<SpendCaps>`.
2767#[derive(Debug, Serialize, Deserialize)]
2768pub struct AdminLiftBreakerArgs {
2769 pub note: String,
2770 pub by: String,
2771}
2772
2773/// `admin_cost_alerts`: the open margin alerts, for sudo's banner.
2774/// Returns `Vec<MarginAlert>`.
2775#[derive(Debug, Default, Serialize, Deserialize)]
2776pub struct AdminCostAlertsArgs {}
2777
2778/// `admin_decide_proposal`: approve or reject a price proposal. An
2779/// approved rise takes effect after the notice period. Returns
2780/// `Outcome<PriceProposal>`.
2781#[derive(Debug, Serialize, Deserialize)]
2782pub struct AdminDecideProposalArgs {
2783 pub id: String,
2784 /// `approve` or `reject`.
2785 pub decision: String,
2786 #[serde(default)]
2787 pub note: String,
2788 pub by: String,
2789}
2790
2791/// `admin_set_cost_settings`. Returns `Outcome<CostSettings>`.
2792#[derive(Debug, Serialize, Deserialize)]
2793pub struct AdminSetCostSettingsArgs {
2794 pub settings: CostSettings,
2795 pub by: String,
2796}
2797
2798/// `admin_set_cost_mapping`: adds, changes or (with `remove`) removes a
2799/// mapping row. Returns `Outcome<CostMapping>`.
2800#[derive(Debug, Serialize, Deserialize)]
2801pub struct AdminSetCostMappingArgs {
2802 pub product: String,
2803 pub meter: String,
2804 #[serde(default)]
2805 pub bucket: String,
2806 #[serde(default)]
2807 pub price_meter: Option<String>,
2808 #[serde(default)]
2809 pub own_meter: Option<String>,
2810 #[serde(default)]
2811 pub scale_to_own: bool,
2812 #[serde(default)]
2813 pub drift_percent: Option<f64>,
2814 #[serde(default)]
2815 pub note: String,
2816 #[serde(default)]
2817 pub remove: bool,
2818 pub by: String,
2819}
2820
2821/// `admin_run_costs`: reads Cloudflare's bill and reconciles now, as the
2822/// daily run does. Returns `Outcome<CostsRun>`.
2823#[derive(Debug, Default, Serialize, Deserialize)]
2824pub struct AdminRunCostsArgs {
2825 #[serde(default)]
2826 pub by: String,
2827}
2828
2829#[derive(Clone, Debug, Default, Serialize, Deserialize)]
2830#[serde(rename_all = "camelCase")]
2831pub struct CostsRun {
2832 pub lines: u32,
2833 pub days: u32,
2834 pub proposals: u32,
2835 pub alerts: u32,
2836 /// What could not be read, in words.
2837 pub problems: Vec<String>,
2838}
2839
2840#[cfg(test)]
2841mod tests {
2842 use super::*;
2843
2844 #[test]
2845 fn an_account_carries_no_run_fee() {
2846 let account = Account {
2847 workspace: "acme".into(),
2848 balance_micros: 0,
2849 status: Status { enabled: true, live: false, free: false },
2850 margin_percent: 20,
2851 card: None,
2852 };
2853 let json = serde_json::to_value(account).unwrap();
2854 let mut keys: Vec<&str> = json.as_object().unwrap().keys().map(String::as_str).collect();
2855 keys.sort_unstable();
2856 assert_eq!(keys, ["balanceMicros", "card", "marginPercent", "status", "workspace"]);
2857 }
2858
2859 #[test]
2860 fn a_price_change_says_when_the_markup_moved() {
2861 let change = PriceChange {
2862 meter: "sandbox_second".into(),
2863 old_cost_micros: 21.0,
2864 new_cost_micros: 21.0,
2865 markup_percent: 20,
2866 old_markup_percent: Some(138),
2867 reason: "Sandbox time is now charged at cost plus 20% from the first second".into(),
2868 created_at: "2026-10-05T00:00:00Z".into(),
2869 effective_at: None,
2870 };
2871 assert_eq!(serde_json::to_value(&change).unwrap()["oldMarkupPercent"], 138);
2872 let cost_only = PriceChange { old_markup_percent: None, ..change };
2873 assert!(serde_json::to_value(&cost_only).unwrap().get("oldMarkupPercent").is_none());
2874 }
2875
2876 #[test]
2877 fn features_are_named_as_the_site_sends_them() {
2878 assert_eq!(
2879 serde_json::to_value(Feature::Deployments).unwrap(),
2880 serde_json::json!("deployments")
2881 );
2882 assert_eq!(Feature::parse("deployments"), Some(Feature::Deployments));
2883 assert_eq!(serde_json::to_value(Feature::Plan).unwrap(), serde_json::json!("plan"));
2884 assert_eq!(Feature::parse("plan"), Some(Feature::Plan));
2885 // Older readers named the plan Team.
2886 assert_eq!(Feature::parse("team"), Some(Feature::Plan));
2887 assert_eq!(serde_json::from_value::<Feature>(serde_json::json!("team")).unwrap(), Feature::Plan);
2888 assert_eq!(Feature::ALL, [Feature::Plan, Feature::Security]);
2889 assert_eq!(Feature::parse("security"), Some(Feature::Security));
2890 assert_eq!(serde_json::to_value(Feature::Security).unwrap(), serde_json::json!("security"));
2891 assert!(SubscriptionStatus::Canceling.on());
2892 assert!(!SubscriptionStatus::PastDue.on());
2893 }
2894
2895 #[test]
2896 fn a_reservation_is_asked_for_and_answered_in_camel_case() {
2897 let asked: ReserveArgs = serde_json::from_value(serde_json::json!({
2898 "workspace": "acme",
2899 "repo": { "namespace": "acme", "name": "web" },
2900 "public": true,
2901 "kind": "check",
2902 "estimateMicros": 2_000_000,
2903 }))
2904 .unwrap();
2905 assert_eq!(asked.kind, ComputeKind::Check);
2906 assert!(asked.kind.open_source_pool());
2907 assert!(!ComputeKind::Agent.open_source_pool());
2908 // Rust callers that write snake_case are read too.
2909 let snake: ReserveArgs = serde_json::from_value(serde_json::json!({
2910 "workspace": "acme",
2911 "repo": { "namespace": "acme", "name": "web" },
2912 "public": false,
2913 "kind": "agent",
2914 "estimate_micros": 1,
2915 }))
2916 .unwrap();
2917 assert_eq!(snake.estimate_micros, 1);
2918 let answer = Reservation { id: "rsv_1".into(), paid_by: PaidBy::OnDemand, held_micros: 5, expires_at: String::new() };
2919 assert_eq!(serde_json::to_value(&answer).unwrap()["paidBy"], "on_demand");
2920 assert_eq!(serde_json::to_value(PlanKind::Internal).unwrap(), "internal");
2921 assert!(!PlanKind::Free.on_demand() && PlanKind::Enterprise.on_demand());
2922 }
2923
2924 #[test]
2925 fn a_refusal_carries_its_own_code() {
2926 let refused: crate::Outcome<Reservation> =
2927 crate::Outcome::fail(crate::FailureCode::OssPoolEmpty, "The open-source pool is spent.");
2928 let json = serde_json::to_value(&refused).unwrap();
2929 assert_eq!(json["error"]["code"], "oss_pool_empty");
2930 assert_eq!(crate::FailureCode::NotPaid.http_status(), 402);
2931 assert_eq!(crate::FailureCode::Paused.http_status(), 409);
2932 }
2933
2934 #[test]
2935 fn who_pays_is_read_as_the_runner_sends_it() {
2936 let run: StartRunArgs = serde_json::from_value(serde_json::json!({
2937 "workspace": "acme",
2938 "repo": { "namespace": "acme", "name": "web" },
2939 "number": 7,
2940 "task": "implement",
2941 "model": "Claude Sonnet 5.5",
2942 "billedTo": "workspace",
2943 }))
2944 .unwrap();
2945 assert_eq!(run.billed_to, "workspace");
2946 }
2947}