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