Skip to content

g1t/crates/contracts/src/billing.rs

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