g1t/crates/contracts/src/billing.rs

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