pr_01m47d15m3e54sn21z27rpy5n9/crates/contracts/src/billing.rs

2,263 lines82,160 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}
274
275#[derive(Clone, Debug, Serialize, Deserialize)]
276#[serde(rename_all = "camelCase")]
277pub struct RunTicket {
278 pub run_id: String,
279 /// Lets the sandbox, and nothing else, report what this run cost.
280 pub token: String,
281}
282
283/// `finish_run`: what a run cost, as its sandbox reports it. Charged once.
284/// Returns `Outcome<bool>`.
285#[derive(Debug, Serialize, Deserialize)]
286#[serde(rename_all = "camelCase")]
287pub struct FinishRunArgs {
288 pub run_id: String,
289 pub token: String,
290 /// What the model provider charged, in US dollars.
291 pub cost_usd: f64,
292 #[serde(default)]
293 pub turns: u32,
294}
295
296
297/// `usage`: what a workspace's agents cost over a period, broken down.
298/// Members only. Returns `Outcome<Usage>`.
299#[derive(Debug, Serialize, Deserialize)]
300pub struct UsageArgs {
301 pub workspace: String,
302 pub viewer: Viewer,
303 /// RFC 3339: the start of the period. The period runs to now.
304 pub since: String,
305}
306
307/// One slice of usage: what it was for, what it cost, how many runs.
308#[derive(Clone, Debug, Serialize, Deserialize)]
309#[serde(rename_all = "camelCase")]
310pub struct UsageSlice {
311 pub key: String,
312 pub micros: i64,
313 pub runs: u32,
314}
315
316/// What a workspace's agents cost over a period.
317#[derive(Clone, Debug, Serialize, Deserialize)]
318#[serde(rename_all = "camelCase")]
319pub struct Usage {
320 pub since: String,
321 /// Charged, including g1t's margin.
322 pub spent_micros: i64,
323 /// What g1t's model provider charged, before the margin.
324 pub cost_micros: i64,
325 /// What runs on the workspace's own provider cost there, as the harness
326 /// estimated it. Not charged by g1t.
327 pub provider_micros: i64,
328 /// What the runs used, at cost: g1t's models and the workspace's own
329 /// provider together, whatever was charged for them.
330 pub used_micros: i64,
331 /// g1t charges nothing for now. The slices then measure usage at cost,
332 /// since every charge is zero.
333 pub free: bool,
334 pub runs: u32,
335 /// Spend per day (`YYYY-MM-DD`) and task, as `day/task` keys.
336 pub by_day: Vec<UsageSlice>,
337 /// Per task: implement, review, revise, update, plan.
338 pub by_task: Vec<UsageSlice>,
339 /// Per repository, `namespace/name`.
340 pub by_repo: Vec<UsageSlice>,
341 /// The pull requests that cost most, as `namespace/name#number`.
342 pub by_pull: Vec<UsageSlice>,
343 /// Per model, by its public name.
344 pub by_model: Vec<UsageSlice>,
345 /// Credit bought in the period.
346 pub added_micros: i64,
347}
348
349/// What a workspace pays a monthly price for. There is one plan, `plan`
350/// ("g1t"): a flat price per workspace, never per person, with included
351/// usage each month, more private storage, and deployments. Never free:
352/// `FREE_WHILE_BUILDING` does not cover it.
353///
354/// `deployments` is not sold on its own any more: it comes with the plan.
355/// A service that asks `has_feature` for it is told whether the workspace
356/// has the plan, and a Deployments subscription bought before the change
357/// keeps working until its period ends.
358#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize)]
359#[serde(rename_all = "snake_case")]
360pub enum Feature {
361 /// The g1t plan. Older readers called it `team`.
362 #[serde(alias = "team")]
363 Plan,
364 /// Previews per pull request and production on g1t.page: part of the
365 /// plan.
366 Deployments,
367}
368
369impl Feature {
370 /// What is sold: the plan alone.
371 pub const ALL: [Feature; 1] = [Feature::Plan];
372
373 pub fn as_str(self) -> &'static str {
374 match self {
375 Feature::Plan => "plan",
376 Feature::Deployments => "deployments",
377 }
378 }
379
380 pub fn parse(name: &str) -> Option<Feature> {
381 match name {
382 "plan" | "team" => Some(Feature::Plan),
383 "deployments" => Some(Feature::Deployments),
384 _ => None,
385 }
386 }
387
388 pub fn title(self) -> &'static str {
389 match self {
390 Feature::Plan => "g1t",
391 Feature::Deployments => "Deployments",
392 }
393 }
394}
395
396/// What the g1t plan includes for deployments each month; usage past it is
397/// charged at cost plus the margin. The billing service describes the plan
398/// with these and the deployments service meters against them.
399pub mod deployments_allowance {
400 /// Apps deployed at once: production and previews together.
401 pub const APPS: u32 = 10;
402 pub const REQUESTS: u64 = 1_000_000;
403 pub const CPU_MS: u64 = 3_000_000;
404 /// What Cloudflare charges g1t past that, in millionths of a dollar.
405 pub const MICROS_PER_APP_MONTH: i64 = 20_000;
406 pub const MICROS_PER_MILLION_REQUESTS: i64 = 300_000;
407 pub const MICROS_PER_MILLION_CPU_MS: i64 = 20_000;
408 /// What one second of a build's sandbox costs g1t (Cloudflare
409 /// Containers, standard-1: half a vCPU, 4 GiB, 8 GB disk), rounded up,
410 /// as the price keeper measured it on 2026-10-05 (14.5). Only a
411 /// fallback: billing charges builds at the price book's `build_second`,
412 /// which the keeper keeps current.
413 pub const MICROS_PER_BUILD_SECOND: i64 = 15;
414 /// Build time the plan includes each month: 200 minutes, about $0.17
415 /// at cost. Builds past it are charged by the second at cost plus the
416 /// margin. Billing's `DEPLOYMENTS_BUILD_SECONDS` overrides it.
417 pub const BUILD_SECONDS: u32 = 12_000;
418 /// Custom domains across the workspace (Cloudflare for SaaS custom
419 /// hostnames); each one past these is charged by the month.
420 pub const CUSTOM_DOMAINS: u32 = 3;
421 /// What one custom hostname costs g1t a month: $0.10.
422 pub const MICROS_PER_DOMAIN_MONTH: i64 = 100_000;
423}
424
425/// `record_sandbox`: how long one sandbox ran for a workspace, reported by
426/// the runner when it stops. Every sandbox g1t starts for a workspace
427/// (agents, reviews, checks, the merge queue, workflow jobs) is metered by
428/// the second, from the first: recorded once per `reference`, with what it
429/// cost g1t, and charged at the price book's `sandbox_second` price unless
430/// `FREE_WHILE_BUILDING`. Deploy builds are charged by the Deployments plan
431/// instead.
432/// Returns `Outcome<bool>`: false if that reference was recorded before.
433#[derive(Debug, Serialize, Deserialize)]
434#[serde(rename_all = "camelCase")]
435pub struct RecordSandboxArgs {
436 pub workspace: String,
437 pub seconds: u32,
438 /// What ran, e.g. `Checks on acme/api#12`.
439 pub description: String,
440 /// `namespace/name`.
441 pub repo: Option<String>,
442 /// Unique to the run.
443 pub reference: String,
444 /// What ran: `agent`, `check`, `workflow` or `queue`. Decides whether
445 /// g1t's open-source pool may pay for it (checks, workflows and the
446 /// merge queue on public repositories). Absent: not the pool.
447 #[serde(default)]
448 pub kind: Option<ComputeKind>,
449 /// The vCPU-seconds the sandbox used, when it can tell. With it, the
450 /// run is priced on its own CPU (`sandbox_base_second` per second plus
451 /// `sandbox_cpu_second` per vCPU-second); without it, at the average
452 /// (`sandbox_second`).
453 #[serde(default, alias = "cpu_seconds")]
454 pub cpu_seconds: Option<f64>,
455 /// The reservation the work started under, settled with this cost.
456 #[serde(default, alias = "reservation_id")]
457 pub reservation_id: Option<String>,
458}
459
460/// How much a workspace has earned g1t's trust with money, which sets how
461/// far its unpaid usage can go before its work stops.
462#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize)]
463#[serde(rename_all = "snake_case")]
464pub enum Trust {
465 /// No live payment yet: only a little past the free allowances.
466 New,
467 /// Has paid g1t real money: the ceiling grows with what it has paid.
468 Paid,
469 /// Has paid steadily for months, with nothing disputed or declined:
470 /// the ceiling follows its monthly spend, up to $10,000, by itself.
471 Established,
472 /// A ceiling g1t set by hand, after talking to the workspace.
473 Reviewed,
474 /// g1t's own workspaces: no ceiling.
475 Internal,
476}
477
478#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize)]
479#[serde(rename_all = "snake_case")]
480pub enum LimitState {
481 Ok,
482 /// Past 80% of the ceiling.
483 Warning,
484 /// At or past it: no new sandboxes, builds or app requests.
485 Stopped,
486}
487
488/// How far a workspace's unpaid usage has gone this month, and where its
489/// work stops: like Fly's or Cloudflare's limits for new accounts, so no
490/// one runs up costs g1t cannot collect. Usage counts at what it cost g1t
491/// or what it is charged, whichever is more, so it counts while g1t is
492/// free too.
493#[derive(Clone, Debug, Serialize, Deserialize)]
494#[serde(rename_all = "camelCase")]
495pub struct Limit {
496 pub workspace: String,
497 /// The account that pays, whose usage and payments the limit counts:
498 /// the workspace's own, or its enterprise's.
499 #[serde(default)]
500 pub account: String,
501 #[serde(default)]
502 pub account_name: String,
503 pub trust: Trust,
504 /// Usage this month (UTC) less what was paid this month.
505 pub exposure_micros: i64,
506 /// Where work stops: the lower of g1t's ceiling and the owner's own
507 /// spend limit. None for g1t's own workspaces.
508 pub ceiling_micros: Option<i64>,
509 /// The ceiling g1t sets from `trust`.
510 pub trust_ceiling_micros: Option<i64>,
511 /// The owner's own monthly limit, if they set one.
512 pub spend_limit_micros: Option<i64>,
513 pub state: LimitState,
514 /// What to tell people when work is stopped or close to it.
515 pub message: Option<String>,
516 /// Charged this month, which the spend limit is measured against.
517 #[serde(default)]
518 pub spent_micros: i64,
519 /// True while the owners have not chosen a spend limit of their own, so
520 /// the automatic one applies: $200, or twice last month's spend.
521 #[serde(default)]
522 pub default_spend_limit: bool,
523 /// The most the owners may set their own limit to: g1t's ceiling. To
524 /// go past it, they contact g1t.
525 #[serde(default)]
526 pub available_micros: Option<i64>,
527 /// How the ceiling grows from here, in a sentence.
528 #[serde(default)]
529 pub growth: Option<String>,
530 /// Money paid in advance and not used yet. It raises what can be used
531 /// before work stops by the same amount, at once.
532 #[serde(default)]
533 pub prepaid_micros: i64,
534 /// The highest ceiling the workspace has ever had. Owners may set their
535 /// spend limit anywhere up to it (plus what is prepaid) without asking.
536 #[serde(default)]
537 pub max_ceiling_micros: Option<i64>,
538 /// The most the owners may raise the limit to themselves, once, with
539 /// `raise_once`: twice the highest ceiling. None once it is used.
540 #[serde(default)]
541 pub raise_once_micros: Option<i64>,
542 /// When the one-time raise was used, RFC 3339.
543 #[serde(default)]
544 pub raised_at: Option<String>,
545 /// True in a paid workspace's first billing cycle, when the ceiling is
546 /// the starting one (`LIMIT_PAID_START_MICROS`).
547 #[serde(default)]
548 pub first_month: bool,
549}
550
551/// `limit`: a workspace's limit, for its members. Returns `Outcome<Limit>`.
552#[derive(Debug, Serialize, Deserialize)]
553pub struct LimitArgs {
554 pub workspace: String,
555 pub viewer: Viewer,
556}
557
558/// `check_limit`: the same, for the services that enforce it. Returns
559/// `Outcome<Limit>`.
560#[derive(Debug, Serialize, Deserialize)]
561pub struct CheckLimitArgs {
562 pub workspace: String,
563}
564
565/// `note_pending`: usage this month that will be charged later, such as
566/// app traffic past a plan, so the workspace's limit counts it now. Each
567/// report replaces the last for that workspace, source and month. Called
568/// by the service that meters it. Returns `bool`.
569#[derive(Debug, Serialize, Deserialize)]
570#[serde(rename_all = "camelCase")]
571pub struct NotePendingArgs {
572 pub workspace: String,
573 /// `deployments`, `security` (scans), `context` (search embeddings) or
574 /// `storage`. Billing charges `security`, `context` and `storage`
575 /// itself once the month is over; `deployments` charges its own.
576 pub source: String,
577 /// What it cost g1t so far this month, before the margin.
578 pub cost_micros: i64,
579}
580
581/// `set_spend_limit`: the owner's own monthly ceiling, under g1t's; None
582/// removes it. Owners only. Returns `Outcome<Limit>`.
583#[derive(Debug, Serialize, Deserialize)]
584#[serde(rename_all = "camelCase")]
585pub struct SetSpendLimitArgs {
586 pub actor: User,
587 pub workspace: String,
588 /// A monthly limit, at most what is available; None goes back to the
589 /// default.
590 pub spend_limit_micros: Option<i64>,
591 /// Use everything available, with no limit of their own.
592 #[serde(default)]
593 pub use_full_limit: bool,
594 /// Use the one-time raise: up to twice the highest ceiling the
595 /// workspace has had, without asking. Once per workspace.
596 #[serde(default, alias = "raiseOnce")]
597 pub raise_once: bool,
598}
599
600/// One metered unit: what it costs g1t, and what it is sold at. The price
601/// is always `cost × (100 + markup) / 100`, so it follows the cost.
602#[derive(Clone, Debug, Serialize, Deserialize)]
603#[serde(rename_all = "camelCase")]
604pub struct Price {
605 /// `sandbox_second`, `build_second`, `app_requests`, `app_cpu`, `app_month`.
606 pub meter: String,
607 pub title: String,
608 pub unit: String,
609 /// Millionths of a dollar per unit; may have a fraction.
610 pub cost_micros: f64,
611 pub markup_percent: u32,
612 pub price_micros: f64,
613 /// `list`: Cloudflare's published price. `cloudflare`: what Cloudflare
614 /// actually billed g1t, measured.
615 pub source: String,
616 /// When it was last checked against Cloudflare's bill.
617 pub checked_at: Option<String>,
618 pub updated_at: String,
619}
620
621impl Price {
622 pub fn price_for(cost_micros: f64, markup_percent: u32) -> f64 {
623 cost_micros * f64::from(100 + markup_percent) / 100.0
624 }
625}
626
627/// A cost that moved.
628#[derive(Clone, Debug, Serialize, Deserialize)]
629#[serde(rename_all = "camelCase")]
630pub struct PriceChange {
631 pub meter: String,
632 pub old_cost_micros: f64,
633 pub new_cost_micros: f64,
634 pub markup_percent: u32,
635 /// The markup before, when the change was to the markup rather than
636 /// to the cost. Absent when the markup stayed `markup_percent`.
637 #[serde(default, skip_serializing_if = "Option::is_none")]
638 pub old_markup_percent: Option<u32>,
639 pub reason: String,
640 pub created_at: String,
641}
642
643/// `prices`: every metered price and the recent changes. Public. Returns
644/// `PriceBook`.
645#[derive(Clone, Debug, Serialize, Deserialize)]
646#[serde(rename_all = "camelCase")]
647pub struct PriceBook {
648 pub prices: Vec<Price>,
649 pub changes: Vec<PriceChange>,
650 /// The margin on model usage, which is charged at what AI Gateway
651 /// priced each request at.
652 pub model_margin_percent: u32,
653 /// Every plan, as it is sold now.
654 #[serde(default)]
655 pub plans: Vec<Plan>,
656 /// What is free, and what pays for it.
657 #[serde(default)]
658 pub free: Option<FreeTier>,
659}
660
661/// What g1t gives without a plan, each with what pays for it: a capped
662/// budget, never an open-ended allowance.
663#[derive(Clone, Debug, Default, Serialize, Deserialize)]
664#[serde(rename_all = "camelCase")]
665pub struct FreeTier {
666 /// Each new workspace's trial credit, once.
667 pub trial_workspace_micros: i64,
668 /// Trial grants each month, in all; new trials wait when it is spent.
669 pub trial_monthly_pool_micros: i64,
670 /// g1t's open-source pool each month, and any one repository's share.
671 pub oss_pool_micros: i64,
672 pub oss_repo_micros: i64,
673 /// Private repository storage before it is charged.
674 pub free_private_storage_bytes: i64,
675 /// Days of audit log, the same on every plan.
676 pub audit_retention_days: u32,
677 /// The smallest amount a card is charged when a month closes; less
678 /// carries over. Charges at a limit always go through.
679 pub min_charge_micros: i64,
680 /// Git operations (clones, fetches and pushes through g1t) included
681 /// each month, on every plan. Past it, the plan pays at cost plus the
682 /// margin; a free workspace is slowed down, never charged.
683 #[serde(default)]
684 pub git_operations_included: u64,
685 /// Past this many in a month, a free workspace's git operations are
686 /// rate-limited.
687 #[serde(default)]
688 pub git_operations_free_cap: u64,
689 /// Private storage on the plan before it is charged.
690 #[serde(default)]
691 pub plan_private_storage_bytes: i64,
692 /// A new paid workspace's ceiling in its first month.
693 #[serde(default)]
694 pub paid_start_ceiling_micros: i64,
695 /// The most a one-click goodwill credit can cost g1t.
696 #[serde(default)]
697 pub overage_forgive_cost_micros: i64,
698}
699
700/// Who pays: a billing account. Every workspace has one; by default its
701/// own. An enterprise account pays for several workspaces at once, as
702/// GitHub Enterprise does: one bill, one limit, one set of terms.
703#[derive(Clone, Debug, Serialize, Deserialize)]
704#[serde(rename_all = "camelCase")]
705pub struct BillingAccount {
706 /// `ws_<slug>` for a workspace's own account; `ent_…` for an enterprise.
707 pub id: String,
708 pub kind: AccountKind,
709 pub name: String,
710 pub terms: Terms,
711 /// The workspaces it pays for.
712 pub workspaces: Vec<String>,
713 /// Where an enterprise's invoices go.
714 #[serde(default)]
715 pub billing_email: Option<String>,
716 /// An enterprise's invoices, newest first. Empty for a workspace's own.
717 #[serde(default)]
718 pub invoices: Vec<EnterpriseInvoice>,
719 pub created_at: String,
720 /// What g1t staff set for the account beyond its terms.
721 #[serde(default)]
722 pub allowances: Allowances,
723}
724
725/// Set per account by g1t staff in sudo, on top of its terms.
726#[derive(Clone, Debug, Default, PartialEq, Serialize, Deserialize)]
727#[serde(rename_all = "camelCase")]
728pub struct Allowances {
729 /// The g1t plan without paying for its monthly price, such as for a
730 /// partner. Usage is charged as usual. Comped accounts have it anyway.
731 #[serde(default, alias = "team")]
732 pub plan: bool,
733 /// Each of the account's public repositories' monthly cap on g1t's
734 /// open-source pool, in place of `OSS_REPO_MICROS`. None: the default.
735 #[serde(default)]
736 pub oss_repo_micros: Option<i64>,
737 /// The trial credit each of its workspaces gets, in place of
738 /// `TRIAL_WORKSPACE_MICROS`, outside the monthly pool. None: the default.
739 #[serde(default)]
740 pub trial_micros: Option<i64>,
741 /// Agents at once, in place of the plan's (2 in the first month or on
742 /// the trial, then 10). None: the default.
743 #[serde(default)]
744 pub max_concurrent_agents: Option<u32>,
745 /// One run's spend cap, in place of `RUN_CAP_MICROS` and the owners'
746 /// own. None: theirs, or the default.
747 #[serde(default)]
748 pub run_cap_micros: Option<i64>,
749 /// What the agents on one issue may spend in all, in place of
750 /// `ISSUE_CAP_MICROS` and the owners' own. None: theirs, or the default.
751 #[serde(default)]
752 pub issue_cap_micros: Option<i64>,
753 /// A hold g1t staff put on new compute, with why. None: no hold.
754 #[serde(default)]
755 pub hold: Option<String>,
756}
757
758/// `admin_set_allowances`: the plan on or off without charge, overrides of
759/// the plan's caps, a hold, and the account's share of g1t's pools.
760/// Recorded with who and why. Returns `Outcome<BillingAccount>`.
761#[derive(Debug, Serialize, Deserialize)]
762pub struct AdminSetAllowancesArgs {
763 pub id: String,
764 pub allowances: Allowances,
765 pub note: String,
766 pub by: String,
767}
768
769// --- Entitlements, and compute started under a reservation -----------------
770//
771// Every service that starts compute (sandboxes for agents, checks,
772// workflows and the merge queue; builds; models; semantic search) asks
773// billing first:
774//
775// 1. `entitlements { workspace }` says what the workspace may do at all:
776// its plan, whether it may start compute, its caps, and whether compute
777// is paused.
778// 2. `reserve { workspace, repo, public, kind, estimate_micros }` holds the
779// work's estimated cost against what may pay for it, so that starts at
780// the same moment cannot overshoot the ceiling together. It answers who
781// pays first, or refuses with a stable code and a message for the owner.
782// 3. `settle { reservation_id, actual_micros }` releases the hold once the
783// work is done. The charge itself goes on the ledger the usual way
784// (`finish_run`, `record_sandbox`, `charge_feature`, `note_pending`).
785//
786// A reservation never settled expires after `RESERVATION_HOURS`.
787
788/// A reservation that is never settled stops holding after this long.
789pub const RESERVATION_HOURS: u64 = 3;
790/// What a ceiling reads as when there is none (g1t's own workspaces): a
791/// billion dollars, which JavaScript holds exactly.
792pub const UNLIMITED_MICROS: i64 = 1_000_000_000_000_000;
793
794/// What a workspace pays g1t on, as far as compute is concerned.
795#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize)]
796#[serde(rename_all = "snake_case")]
797pub enum PlanKind {
798 /// No plan: the forge is free; compute only from a trial or g1t's
799 /// open-source pool, after a card check.
800 Free,
801 /// The g1t plan, paid for (or given by g1t staff without its price).
802 Paid,
803 /// g1t's own workspaces and Flagon's (comped terms): the plan without
804 /// being charged. Usage is still recorded at what it cost.
805 Internal,
806 /// Paid for by an enterprise account, invoiced.
807 Enterprise,
808}
809
810impl PlanKind {
811 pub fn as_str(self) -> &'static str {
812 match self {
813 PlanKind::Free => "free",
814 PlanKind::Paid => "paid",
815 PlanKind::Internal => "internal",
816 PlanKind::Enterprise => "enterprise",
817 }
818 }
819
820 /// Whether usage past what is included may be charged (on demand).
821 pub fn on_demand(self) -> bool {
822 !matches!(self, PlanKind::Free)
823 }
824}
825
826/// What compute is for.
827#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize)]
828#[serde(rename_all = "snake_case")]
829pub enum ComputeKind {
830 /// An agent's run: its sandbox and its model.
831 Agent,
832 /// Checks on a pull request.
833 Check,
834 /// A workflow job.
835 Workflow,
836 /// The merge queue's checks.
837 Queue,
838 /// A deployment's build.
839 Deploy,
840 /// Semantic search: embeddings in the context hub.
841 Embedding,
842}
843
844impl ComputeKind {
845 pub fn as_str(self) -> &'static str {
846 match self {
847 ComputeKind::Agent => "agent",
848 ComputeKind::Check => "check",
849 ComputeKind::Workflow => "workflow",
850 ComputeKind::Queue => "queue",
851 ComputeKind::Deploy => "deploy",
852 ComputeKind::Embedding => "embedding",
853 }
854 }
855
856 /// Whether g1t's open-source pool may pay for it on a public
857 /// repository: checks, workflows and the merge queue only.
858 pub fn open_source_pool(self) -> bool {
859 matches!(self, ComputeKind::Check | ComputeKind::Workflow | ComputeKind::Queue)
860 }
861}
862
863/// Who pays first for reserved work. What the first source cannot cover
864/// falls to the next, in this order.
865#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize)]
866#[serde(rename_all = "snake_case")]
867pub enum PaidBy {
868 /// The plan's included usage this month.
869 Credit,
870 /// The workspace's one-time trial credit.
871 Trial,
872 /// g1t's open-source pool.
873 Oss,
874 /// Charged to the workspace, at cost plus the margin.
875 OnDemand,
876}
877
878/// `entitlements`: what a workspace may do now, for the services that
879/// start compute and the pages that show it. Takes `EntitlementsArgs`;
880/// returns `Entitlements`. No viewer: callers decide who sees it.
881#[derive(Debug, Serialize, Deserialize)]
882pub struct EntitlementsArgs {
883 pub workspace: String,
884}
885
886#[derive(Clone, Debug, Serialize, Deserialize)]
887#[serde(rename_all = "camelCase")]
888pub struct Entitlements {
889 pub workspace: String,
890 pub plan: PlanKind,
891 /// May start sandboxes, models, deployments and semantic search at all:
892 /// paid, internal and enterprise workspaces, or a free one with trial
893 /// credit left. A free workspace may still use the open-source pool
894 /// for checks, workflows and the merge queue on public repositories
895 /// after a card check; `reserve` decides that per start.
896 pub compute: bool,
897 /// The one-time trial credit left; 0 if none was granted or it is used.
898 pub trial_micros_left: i64,
899 /// A card check has been done. The trial and the open-source pool need
900 /// it.
901 pub trial_verified: bool,
902 /// A paid workspace still in its first billing cycle.
903 pub first_month: bool,
904 /// Agents at once: 2 in the first month or on the trial, 10 after;
905 /// staff can override it.
906 pub max_concurrent_agents: u32,
907 /// The longest one run may take: 60 minutes in the first month or on
908 /// the trial; otherwise the guardrails' own caps (`MAX_MINUTES`).
909 pub max_run_minutes: u32,
910 /// One run's spend cap (`RUN_CAP_MICROS`, $2 by default); staff can
911 /// override it.
912 pub run_cap_micros: i64,
913 /// What agents may spend on one issue in all (`ISSUE_CAP_MICROS`, $10
914 /// by default); the owners can set it (`set_caps`), and staff override.
915 pub issue_cap_micros: i64,
916 /// Where on-demand work stops: g1t's ceiling on usage not yet paid
917 /// for. `UNLIMITED_MICROS` for g1t's own workspaces; 0 for a free one,
918 /// which has no on-demand usage.
919 pub ceiling_micros: i64,
920 /// Usage not yet paid for this month, with prepayment taken off.
921 pub exposure_micros: i64,
922 /// Why new compute is paused, for the owner: the limit is reached, a
923 /// spend spike is waiting for an owner to confirm it, or g1t staff put
924 /// a hold on it. None when it is not.
925 pub paused: Option<String>,
926 // What the workspace's plan gives it, for its pages.
927 /// What open reservations hold now.
928 #[serde(default)]
929 pub held_micros: i64,
930 /// Paid in advance and not used yet.
931 #[serde(default)]
932 pub prepaid_micros: i64,
933 /// The plan's included usage each month, and what of it is used.
934 #[serde(default)]
935 pub included_micros: i64,
936 #[serde(default)]
937 pub included_used_micros: i64,
938 /// How far back the audit log can be read and exported: the same on
939 /// every plan.
940 pub audit_retention_days: u32,
941 /// Private repository storage included before it is charged.
942 pub free_private_storage_bytes: i64,
943 /// The last daily measure of the workspace's private repositories.
944 pub private_storage_bytes: i64,
945 /// What g1t's open-source pool paid for the workspace this month.
946 pub oss_paid_micros: i64,
947 /// Build time the plan includes each month, and used.
948 pub build_seconds_included: u32,
949 pub build_seconds_used: u32,
950 /// Git operations this month, and how many are included.
951 #[serde(default)]
952 pub git_operations: u64,
953 #[serde(default)]
954 pub git_operations_included: u64,
955 /// The smallest amount a card is charged when a month closes.
956 pub min_charge_micros: i64,
957 /// A spend spike waiting for an owner, or decided.
958 #[serde(default)]
959 pub spike: Option<Spike>,
960 /// Where usage stands against what is included and the limits, from 50%.
961 #[serde(default)]
962 pub alerts: Vec<UsageAlert>,
963}
964
965/// One level reached: 50, 75, 90 or 100 percent of something.
966#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
967#[serde(rename_all = "camelCase")]
968pub struct UsageAlert {
969 /// `included` (the plan's included usage), `spend_limit` (the owners'
970 /// own limit) or `ceiling` (g1t's, on usage not yet paid for).
971 pub meter: String,
972 pub level: u32,
973 pub used_micros: i64,
974 pub limit_micros: i64,
975 pub message: String,
976}
977
978/// An hour's spend well above the workspace's usual pace: new compute
979/// waits until an owner says to keep going.
980#[derive(Clone, Debug, Serialize, Deserialize)]
981#[serde(rename_all = "camelCase")]
982pub struct Spike {
983 pub id: String,
984 /// `open` (waiting for an owner), `continued` (an owner said keep
985 /// going) or `stopped` (an owner said stop).
986 pub status: String,
987 /// The hour's spend when it was found, and the usual hour's.
988 pub hour_micros: i64,
989 pub average_micros: i64,
990 pub detected_at: String,
991 #[serde(default)]
992 pub decided_by: Option<String>,
993 #[serde(default)]
994 pub decided_at: Option<String>,
995 /// While continued: until when, unless spend doubles again first.
996 #[serde(default)]
997 pub until: Option<String>,
998}
999
1000/// `reserve`: holds an estimate of a start's cost before the work starts.
1001/// Returns `Outcome<Reservation>`, or a failure whose code says why not:
1002///
1003/// - `paused`: a spend spike waiting for an owner, or a hold.
1004/// - `limit`: the spend limit or g1t's ceiling would be passed.
1005/// - `not_paid`: no plan, and nothing else pays for this kind of work (or
1006/// no card check yet).
1007/// - `trial_used`: the one-time trial is spent.
1008/// - `oss_pool_empty`: the open-source pool, or the repository's share of
1009/// it, is spent this month.
1010///
1011/// The message says exactly what to do, with the page to do it on (such as
1012/// `/acme/-/billing`).
1013#[derive(Debug, Serialize, Deserialize)]
1014#[serde(rename_all = "camelCase")]
1015pub struct ReserveArgs {
1016 pub workspace: String,
1017 pub repo: RepoPath,
1018 /// Whether the repository is public: the open-source pool pays only for
1019 /// public repositories' checks, workflows and merge queue.
1020 pub public: bool,
1021 pub kind: ComputeKind,
1022 /// The most the work is expected to cost g1t, before the margin, in
1023 /// millionths of a dollar (billing adds the margin, as it does to every
1024 /// charge). For an agent, its model's average plus its sandbox for its
1025 /// whole time cap.
1026 #[serde(alias = "estimate_micros")]
1027 pub estimate_micros: i64,
1028}
1029
1030#[derive(Clone, Debug, Serialize, Deserialize)]
1031#[serde(rename_all = "camelCase")]
1032pub struct Reservation {
1033 pub id: String,
1034 pub paid_by: PaidBy,
1035 /// What is held, at cost; less than the estimate when a free
1036 /// workspace's last bit of trial credit is all there is.
1037 #[serde(default)]
1038 pub held_micros: i64,
1039 /// RFC 3339: when the hold lapses if never settled.
1040 #[serde(default)]
1041 pub expires_at: String,
1042}
1043
1044/// `settle`: releases a reservation's hold with what the work cost. The
1045/// charge goes on the ledger the usual way. Safe to repeat. Returns
1046/// `Outcome<bool>`: false if it was settled or had lapsed before.
1047#[derive(Debug, Serialize, Deserialize)]
1048#[serde(rename_all = "camelCase")]
1049pub struct SettleArgs {
1050 #[serde(alias = "reservation_id")]
1051 pub reservation_id: String,
1052 /// What the work cost g1t, before the margin.
1053 #[serde(alias = "actual_micros")]
1054 pub actual_micros: i64,
1055}
1056
1057// --- Card checks, the plan, prepayment -------------------------------------
1058
1059/// `card_check`: starts Stripe's page to save and verify a card: a setup
1060/// with 3-D Secure where the card supports it, which the card's bank sees
1061/// as a $0 or $1 authorization that is never charged. The trial and the
1062/// open-source pool need it, and it is the card the plan uses. Owners only.
1063/// Returns `Outcome<Checkout>`; the page's id comes back to `return_url` as
1064/// `session`, for `confirm_card_check`.
1065#[derive(Debug, Serialize, Deserialize)]
1066#[serde(rename_all = "camelCase")]
1067pub struct CardCheckArgs {
1068 pub actor: User,
1069 pub workspace: String,
1070 #[serde(alias = "return_url")]
1071 pub return_url: String,
1072}
1073
1074/// `confirm_card_check`: records the check once Stripe says the card was
1075/// verified, and grants the trial if the month's pool has room and the card
1076/// has not had one before. Safe to repeat. Returns `Outcome<Entitlements>`.
1077#[derive(Debug, Serialize, Deserialize)]
1078pub struct ConfirmCardCheckArgs {
1079 pub workspace: String,
1080 pub viewer: Viewer,
1081 pub session: String,
1082}
1083
1084// --- Limits: raising them, and spikes ---------------------------------------
1085
1086/// A request to g1t: a higher limit, or help with usage that went past
1087/// what was meant.
1088#[derive(Clone, Debug, Serialize, Deserialize)]
1089#[serde(rename_all = "camelCase")]
1090pub struct LimitRequest {
1091 pub id: String,
1092 pub workspace: String,
1093 /// `limit` (raise my limit) or `overage` (spent more than meant to).
1094 pub kind: String,
1095 /// The limit asked for; for an overage, what they think went wrong.
1096 pub amount_micros: i64,
1097 pub reason: String,
1098 pub expected_monthly_micros: i64,
1099 /// `open`, `approved` or `declined`.
1100 pub status: String,
1101 /// What was approved, which may differ from what was asked.
1102 #[serde(default)]
1103 pub decided_micros: Option<i64>,
1104 #[serde(default)]
1105 pub decided_by: Option<String>,
1106 /// The answer, as the owner sees it.
1107 #[serde(default)]
1108 pub answer: Option<String>,
1109 pub created_by: String,
1110 pub created_at: String,
1111 #[serde(default)]
1112 pub decided_at: Option<String>,
1113}
1114
1115/// `request_limit`: an owner asks g1t for more, or for help with usage past
1116/// what they meant. Answered within one business day, in the app and by
1117/// email. Owners only. Returns `Outcome<LimitRequest>`.
1118#[derive(Debug, Serialize, Deserialize)]
1119#[serde(rename_all = "camelCase")]
1120pub struct RequestLimitArgs {
1121 pub actor: User,
1122 pub workspace: String,
1123 /// `limit` or `overage`.
1124 pub kind: String,
1125 #[serde(alias = "amount_micros")]
1126 pub amount_micros: i64,
1127 pub reason: String,
1128 #[serde(default, alias = "expected_monthly_micros")]
1129 pub expected_monthly_micros: i64,
1130}
1131
1132/// `limit_requests`: a workspace's requests, newest first. Members only.
1133/// Returns `Outcome<Vec<LimitRequest>>`.
1134#[derive(Debug, Serialize, Deserialize)]
1135pub struct LimitRequestsArgs {
1136 pub workspace: String,
1137 pub viewer: Viewer,
1138}
1139
1140/// `confirm_spike`: an owner's answer to a spend spike. Keep going lifts the
1141/// pause for 24 hours, or until the hour's spend doubles again; stop keeps
1142/// new compute paused until an owner says to keep going. Owners only.
1143/// Returns `Outcome<Entitlements>`.
1144#[derive(Debug, Serialize, Deserialize)]
1145#[serde(rename_all = "camelCase")]
1146pub struct ConfirmSpikeArgs {
1147 pub actor: User,
1148 pub workspace: String,
1149 #[serde(alias = "keep_going")]
1150 pub keep_going: bool,
1151}
1152
1153/// `set_caps`: the owners' own caps on agents: one run's spend ($0.10 to
1154/// $100) and what the agents on one issue may spend in all ($1 to $1,000).
1155/// None goes back to the default ($2 and $10). A cap g1t staff set for the
1156/// account wins over both. Owners only. Returns `Outcome<Entitlements>`.
1157#[derive(Debug, Serialize, Deserialize)]
1158#[serde(rename_all = "camelCase")]
1159pub struct SetCapsArgs {
1160 pub actor: User,
1161 pub workspace: String,
1162 #[serde(default, alias = "run_cap_micros")]
1163 pub run_cap_micros: Option<i64>,
1164 #[serde(default, alias = "issue_cap_micros")]
1165 pub issue_cap_micros: Option<i64>,
1166}
1167
1168/// What staff see beside a request: the workspace's history with g1t.
1169#[derive(Clone, Debug, Default, Serialize, Deserialize)]
1170#[serde(rename_all = "camelCase")]
1171pub struct WorkspaceHistory {
1172 pub plan: Option<PlanKind>,
1173 /// The last six months, oldest first.
1174 pub months: Vec<MonthFigures>,
1175 /// Live payments that have cleared, and how many.
1176 pub paid_cleared_micros: i64,
1177 pub payments: u32,
1178 pub disputes: u32,
1179 pub declines: u32,
1180 /// The first time the workspace appears in billing, RFC 3339.
1181 pub first_seen: Option<String>,
1182 pub ceiling_micros: Option<i64>,
1183 pub max_ceiling_micros: Option<i64>,
1184 pub spend_limit_micros: Option<i64>,
1185 /// Recent velocity: the last hour, the usual hour over the last week,
1186 /// and the last 24 hours, at price.
1187 pub last_hour_micros: i64,
1188 pub average_hour_micros: i64,
1189 pub last_day_micros: i64,
1190}
1191
1192#[derive(Clone, Debug, Serialize, Deserialize)]
1193#[serde(rename_all = "camelCase")]
1194pub struct LimitRequestReview {
1195 pub request: LimitRequest,
1196 pub history: WorkspaceHistory,
1197}
1198
1199/// `admin_limit_requests`: requests for staff, oldest open first. Returns
1200/// `Vec<LimitRequestReview>`.
1201#[derive(Debug, Default, Serialize, Deserialize)]
1202pub struct AdminLimitRequestsArgs {
1203 /// `open` (the default), `approved`, `declined` or `all`.
1204 #[serde(default)]
1205 pub status: Option<String>,
1206}
1207
1208/// `admin_decide_limit_request`: approve (at the amount asked, or
1209/// `amount_micros`) or decline. The owner is told in the app and by email.
1210/// Recorded with who and why. Returns `Outcome<LimitRequest>`.
1211#[derive(Debug, Serialize, Deserialize)]
1212pub struct AdminDecideLimitRequestArgs {
1213 pub id: String,
1214 /// `approve` or `decline`.
1215 pub decision: String,
1216 #[serde(default)]
1217 pub amount_micros: Option<i64>,
1218 /// What the owner is told, beside the decision.
1219 #[serde(default)]
1220 pub note: String,
1221 pub by: String,
1222}
1223
1224/// `admin_record_payment`: money that reached g1t outside the card pages,
1225/// such as a bank transfer, entered as a payment (it raises the limit like
1226/// one). Recorded with who and the transfer's reference. Returns
1227/// `Outcome<LedgerEntry>`.
1228#[derive(Debug, Serialize, Deserialize)]
1229pub struct AdminRecordPaymentArgs {
1230 pub workspace: String,
1231 pub amount_micros: i64,
1232 /// The bank's reference for the transfer, or Stripe's payment id.
1233 pub reference: String,
1234 pub note: String,
1235 pub by: String,
1236}
1237
1238// --- Overages and goodwill (sudo) --------------------------------------------
1239
1240/// What a one-time goodwill credit would come to: g1t's margin on the
1241/// overage, always, plus as much of its underlying cost as the cap allows.
1242#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, Serialize, Deserialize)]
1243#[serde(rename_all = "camelCase")]
1244pub struct Goodwill {
1245 /// This month's charges above the workspace's typical month.
1246 pub overage_micros: i64,
1247 /// The part of the overage that is g1t's margin.
1248 pub margin_micros: i64,
1249 /// The part that is what g1t paid its providers.
1250 pub cost_micros: i64,
1251 /// The one-click credit: the margin plus the cost up to the cap.
1252 pub credit_micros: i64,
1253 /// Of the credit, the real cost g1t absorbs.
1254 pub absorbed_micros: i64,
1255}
1256
1257/// A workspace whose month went well past its usual, or hit a spike.
1258#[derive(Clone, Debug, Serialize, Deserialize)]
1259#[serde(rename_all = "camelCase")]
1260pub struct Overage {
1261 pub workspace: String,
1262 pub plan: PlanKind,
1263 /// The median of its last three months' charges.
1264 pub typical_month_micros: i64,
1265 pub this_month_micros: i64,
1266 /// What this month cost g1t, and what g1t keeps of it.
1267 pub cost_micros: i64,
1268 pub margin_micros: i64,
1269 /// A spike this month, if there was one.
1270 pub spike: Option<Spike>,
1271 /// The runs that cost the most this month.
1272 pub top_entries: Vec<LedgerEntry>,
1273 pub goodwill: Goodwill,
1274 /// False when a goodwill credit was given in the last 12 months.
1275 pub goodwill_available: bool,
1276 pub last_goodwill_at: Option<String>,
1277 /// An open overage request from the owner, if there is one.
1278 pub request: Option<LimitRequest>,
1279}
1280
1281/// `admin_overages`: the Overages queue. Returns `Vec<Overage>`.
1282#[derive(Debug, Default, Serialize, Deserialize)]
1283pub struct AdminOveragesArgs {}
1284
1285/// `admin_goodwill`: credits a workspace for accidental usage. With no
1286/// amount, the one-click credit (`Goodwill::credit_micros`), once per
1287/// workspace in 12 months. A larger amount, or a second within 12 months,
1288/// needs a typed reason. It shows on the statement as "Credit from g1t:
1289/// accidental usage on <date>". Returns `Outcome<LedgerEntry>`.
1290#[derive(Debug, Serialize, Deserialize)]
1291pub struct AdminGoodwillArgs {
1292 pub workspace: String,
1293 #[serde(default)]
1294 pub amount_micros: Option<i64>,
1295 /// Why, typed by staff; needed past the one-click credit.
1296 #[serde(default)]
1297 pub reason: String,
1298 /// The day the accidental usage happened, `YYYY-MM-DD`; today if absent.
1299 #[serde(default)]
1300 pub day: Option<String>,
1301 pub by: String,
1302}
1303
1304/// One workspace's recent pace, for sudo's velocity view.
1305#[derive(Clone, Debug, Serialize, Deserialize)]
1306#[serde(rename_all = "camelCase")]
1307pub struct Velocity {
1308 pub workspace: String,
1309 pub plan: PlanKind,
1310 pub last_hour_micros: i64,
1311 pub average_hour_micros: i64,
1312 pub last_day_micros: i64,
1313 pub this_month_micros: i64,
1314 /// The last hour over the usual hour; 0 with no history.
1315 pub ratio: f64,
1316 pub spike: Option<Spike>,
1317 pub first_seen: Option<String>,
1318}
1319
1320/// `admin_velocity`: workspaces spending in the last day, fastest first.
1321/// Returns `Vec<Velocity>`.
1322#[derive(Debug, Default, Serialize, Deserialize)]
1323pub struct AdminVelocityArgs {}
1324
1325#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize)]
1326#[serde(rename_all = "snake_case")]
1327pub enum AccountKind {
1328 Workspace,
1329 Enterprise,
1330}
1331
1332/// How an account is charged. Standard unless g1t set otherwise in sudo.
1333#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
1334#[serde(rename_all = "camelCase")]
1335pub struct Terms {
1336 pub kind: TermsKind,
1337 /// Off every usage charge, in percent. Custom terms only.
1338 #[serde(default)]
1339 pub discount_percent: u32,
1340 /// A ceiling on unpaid usage that replaces the one trust would give.
1341 #[serde(default)]
1342 pub ceiling_micros: Option<i64>,
1343 /// Why, for whoever looks next.
1344 #[serde(default)]
1345 pub note: String,
1346 /// When the terms end and the account goes back to standard.
1347 #[serde(default)]
1348 pub until: Option<String>,
1349 #[serde(default)]
1350 pub set_by: Option<String>,
1351 #[serde(default)]
1352 pub set_at: Option<String>,
1353}
1354
1355impl Terms {
1356 pub fn standard() -> Self {
1357 Terms {
1358 kind: TermsKind::Standard,
1359 discount_percent: 0,
1360 ceiling_micros: None,
1361 note: String::new(),
1362 until: None,
1363 set_by: None,
1364 set_at: None,
1365 }
1366 }
1367
1368 /// What a charge becomes under these terms.
1369 pub fn apply(&self, charge_micros: i64) -> i64 {
1370 match self.kind {
1371 TermsKind::Comped => 0,
1372 TermsKind::Custom => charge_micros * i64::from(100 - self.discount_percent.min(100)) / 100,
1373 TermsKind::Standard => charge_micros,
1374 }
1375 }
1376}
1377
1378#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize)]
1379#[serde(rename_all = "snake_case")]
1380pub enum TermsKind {
1381 /// Prices as published, limits by trust.
1382 Standard,
1383 /// Nothing charged; usage still recorded with its cost. Paid features
1384 /// are on without a plan. For g1t's own workspaces, partners, and the
1385 /// like.
1386 Comped,
1387 /// A discount, a ceiling, or both.
1388 Custom,
1389}
1390
1391/// `stripe_webhook`: an event from Stripe, as the API received it: the raw
1392/// body and its `Stripe-Signature` header. Billing checks the signature
1393/// against the secret of the endpoint it registered, and handles each
1394/// event once. Returns `Outcome<bool>`: false for one already handled.
1395#[derive(Debug, Serialize, Deserialize)]
1396pub struct StripeWebhookArgs {
1397 pub payload: String,
1398 pub signature: String,
1399}
1400
1401/// `admin_stripe`: where billing stands with Stripe. Staff only. Returns
1402/// `StripeStatus`. With `setup: true`, registers (or replaces) the webhook
1403/// endpoint for the current mode first.
1404#[derive(Debug, Default, Serialize, Deserialize)]
1405pub struct AdminStripeArgs {
1406 #[serde(default)]
1407 pub setup: bool,
1408 #[serde(default)]
1409 pub by: Option<String>,
1410}
1411
1412#[derive(Clone, Debug, Serialize, Deserialize)]
1413#[serde(rename_all = "camelCase")]
1414pub struct StripeStatus {
1415 /// `test` or `live`, from the key; `off` without one.
1416 pub mode: String,
1417 pub webhook: Option<StripeWebhook>,
1418 /// The latest events handled, newest first.
1419 pub recent_events: Vec<StripeEventSummary>,
1420 /// What went wrong setting up, if it did.
1421 pub error: Option<String>,
1422}
1423
1424#[derive(Clone, Debug, Serialize, Deserialize)]
1425#[serde(rename_all = "camelCase")]
1426pub struct StripeWebhook {
1427 pub url: String,
1428 pub endpoint_id: String,
1429 pub events: Vec<String>,
1430 pub created_by: String,
1431 pub created_at: String,
1432}
1433
1434#[derive(Clone, Debug, Serialize, Deserialize)]
1435#[serde(rename_all = "camelCase")]
1436pub struct StripeEventSummary {
1437 pub id: String,
1438 pub kind: String,
1439 pub outcome: String,
1440 pub received_at: String,
1441}
1442
1443/// `admin_enterprise_billing`: where an enterprise's invoices go. Creates
1444/// or updates its Stripe customer. Returns `Outcome<BillingAccount>`.
1445#[derive(Debug, Serialize, Deserialize)]
1446pub struct AdminEnterpriseBillingArgs {
1447 pub id: String,
1448 pub email: String,
1449 pub by: String,
1450}
1451
1452/// `admin_invoice_enterprise`: sends an enterprise its invoice now, for
1453/// what its workspaces owe, rather than waiting for the month to close.
1454/// Returns `Outcome<EnterpriseInvoice>`.
1455#[derive(Debug, Serialize, Deserialize)]
1456pub struct AdminInvoiceEnterpriseArgs {
1457 pub id: String,
1458 pub by: String,
1459}
1460
1461/// An enterprise's invoice: one line per workspace, paid on Stripe.
1462#[derive(Clone, Debug, Serialize, Deserialize)]
1463#[serde(rename_all = "camelCase")]
1464pub struct EnterpriseInvoice {
1465 pub invoice_id: String,
1466 /// Stripe's page for it, where it is paid.
1467 pub hosted_url: Option<String>,
1468 pub amount_micros: i64,
1469 /// `open`, `paid`, `overdue` or `void`.
1470 pub status: String,
1471 pub period: String,
1472 pub lines: Vec<InvoiceLine>,
1473 pub created_at: String,
1474}
1475
1476#[derive(Clone, Debug, Serialize, Deserialize)]
1477#[serde(rename_all = "camelCase")]
1478pub struct InvoiceLine {
1479 pub workspace: String,
1480 pub amount_micros: i64,
1481}
1482
1483/// A workspace's invoice from g1t: one per month, and one each time it is
1484/// charged near its limit. Itemised, charged to the card on file, and kept
1485/// in Stripe's billing page with its PDF.
1486#[derive(Clone, Debug, Serialize, Deserialize)]
1487#[serde(rename_all = "camelCase")]
1488pub struct WorkspaceInvoice {
1489 pub invoice_id: String,
1490 pub workspace: String,
1491 /// `month` (2026-10) or `threshold`.
1492 pub reason: String,
1493 pub period: String,
1494 pub amount_micros: i64,
1495 /// `paid`, `open`, `failed` or `void`.
1496 pub status: String,
1497 pub hosted_url: Option<String>,
1498 pub pdf_url: Option<String>,
1499 pub lines: Vec<InvoiceItem>,
1500 pub created_at: String,
1501}
1502
1503#[derive(Clone, Debug, Serialize, Deserialize)]
1504#[serde(rename_all = "camelCase")]
1505pub struct InvoiceItem {
1506 pub description: String,
1507 pub amount_micros: i64,
1508}
1509
1510/// `invoices`: a workspace's invoices from g1t, newest first. Members
1511/// only. Returns `Outcome<Vec<WorkspaceInvoice>>`.
1512#[derive(Debug, Serialize, Deserialize)]
1513pub struct InvoicesArgs {
1514 pub workspace: String,
1515 pub viewer: Viewer,
1516}
1517
1518/// `admin_workspace_invoices`: the same, for staff. Returns
1519/// `Vec<WorkspaceInvoice>`.
1520#[derive(Debug, Serialize, Deserialize)]
1521pub struct AdminWorkspaceInvoicesArgs {
1522 pub workspace: String,
1523}
1524
1525/// `statement`: a month of a workspace's ledger, grouped by day (or by
1526/// project) with a line per kind of charge. Members only. Returns
1527/// `Outcome<Statement>`.
1528#[derive(Debug, Serialize, Deserialize)]
1529pub struct StatementArgs {
1530 pub workspace: String,
1531 pub viewer: Viewer,
1532 /// YYYY-MM; this month when absent.
1533 #[serde(default)]
1534 pub month: Option<String>,
1535 /// `day` (the default) or `project`.
1536 #[serde(default)]
1537 pub group: Option<String>,
1538}
1539
1540#[derive(Clone, Debug, Serialize, Deserialize)]
1541#[serde(rename_all = "camelCase")]
1542pub struct Statement {
1543 pub month: String,
1544 /// Months with any entries, newest first.
1545 pub months: Vec<String>,
1546 pub groups: Vec<StatementGroup>,
1547 pub totals: StatementTotals,
1548}
1549
1550#[derive(Clone, Debug, Serialize, Deserialize)]
1551#[serde(rename_all = "camelCase")]
1552pub struct StatementGroup {
1553 /// The day (YYYY-MM-DD) or the project (`owner/name`, or empty).
1554 pub key: String,
1555 pub label: String,
1556 pub lines: Vec<StatementLine>,
1557 /// What the group's charges come to.
1558 pub charged_micros: i64,
1559}
1560
1561#[derive(Clone, Debug, Serialize, Deserialize)]
1562#[serde(rename_all = "camelCase")]
1563pub struct StatementLine {
1564 /// Agent runs, Sandbox time, Deployments, Payments, Credits from g1t,
1565 /// Refunds, and, for older entries, Runs on your own model provider.
1566 pub kind: String,
1567 pub count: u32,
1568 /// Charges positive; money in (payments, credits) negative.
1569 pub charged_micros: i64,
1570 pub cost_micros: i64,
1571 /// Of the usage on the line, what was paid for before it was charged:
1572 /// by the plan's included usage, the trial credit, g1t's open-source
1573 /// pool, or g1t itself. Not in `charged_micros`.
1574 #[serde(default)]
1575 pub covered_micros: i64,
1576}
1577
1578#[derive(Clone, Debug, Serialize, Deserialize)]
1579#[serde(rename_all = "camelCase")]
1580pub struct StatementTotals {
1581 pub charged_micros: i64,
1582 pub paid_micros: i64,
1583 pub cost_micros: i64,
1584 pub entries: u32,
1585 /// What paid for usage before it was charged, one line per source,
1586 /// such as "Paid by g1t's open-source pool".
1587 #[serde(default)]
1588 pub covered: Vec<Covered>,
1589 /// Owed when the month closed but under the minimum charge, so it
1590 /// carries over to the next invoice. Zero when nothing carried.
1591 #[serde(default)]
1592 pub carried_micros: i64,
1593}
1594
1595/// One source that paid for usage before it was charged.
1596#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
1597#[serde(rename_all = "camelCase")]
1598pub struct Covered {
1599 /// `included`, `trial`, `oss_pool` or `given`.
1600 pub source: String,
1601 /// "Paid by your plan's included usage", "Paid by your trial credit",
1602 /// "Paid by g1t's open-source pool", "Covered by g1t".
1603 pub label: String,
1604 pub micros: i64,
1605}
1606
1607/// `statement_entries`: one statement line's entries, newest first, 50 at
1608/// a time (`before` = the last id seen). Returns `Outcome<Vec<LedgerEntry>>`.
1609#[derive(Debug, Serialize, Deserialize)]
1610pub struct StatementEntriesArgs {
1611 pub workspace: String,
1612 pub viewer: Viewer,
1613 pub month: String,
1614 pub kind: String,
1615 #[serde(default)]
1616 pub day: Option<String>,
1617 #[serde(default)]
1618 pub project: Option<String>,
1619 #[serde(default)]
1620 pub before: Option<String>,
1621}
1622
1623// --- Sales (sudo.g1t.sh) ------------------------------------------------------
1624//
1625// What staff need to know to reach out: who is growing, who is close to
1626// their limit, who was declined, who has become a steady customer. And what
1627// was done about it: a stage, an owner on g1t's side, a next step, notes.
1628
1629/// Why a workspace is worth a look.
1630#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize)]
1631#[serde(rename_all = "snake_case")]
1632pub enum SignalKind {
1633 /// At its limit, or its own spend limit: work is stopped.
1634 AtLimit,
1635 /// Past 80% of what is available to it: about to need more.
1636 NearCeiling,
1637 /// Its card was declined or a payment disputed.
1638 Declined,
1639 /// This month is well ahead of last month.
1640 Growing,
1641 /// Became Established: the ceiling now follows its spend.
1642 Established,
1643 /// Paid g1t for the first time.
1644 FirstPayment,
1645 /// Spending enough that custom terms or an enterprise may suit it.
1646 HighSpend,
1647}
1648
1649#[derive(Clone, Debug, Serialize, Deserialize)]
1650#[serde(rename_all = "camelCase")]
1651pub struct Signal {
1652 pub workspace: String,
1653 pub kind: SignalKind,
1654 /// One sentence, with the figures.
1655 pub detail: String,
1656 /// The figure that matters, such as this month's spend.
1657 pub value_micros: i64,
1658 /// Its sales stage, if staff gave it one.
1659 pub stage: Option<String>,
1660 pub owner: Option<String>,
1661 #[serde(default)]
1662 pub next_step: Option<String>,
1663 /// When the next step is due, `YYYY-MM-DD`.
1664 #[serde(default)]
1665 pub next_at: Option<String>,
1666}
1667
1668/// `admin_invoices`: every invoice g1t has sent, workspaces' and
1669/// enterprises', newest first. Returns `Vec<InvoiceSummary>`.
1670#[derive(Debug, Default, Serialize, Deserialize)]
1671pub struct AdminInvoicesArgs {
1672 /// `paid`, `open`, `failed`, `overdue` or `void`.
1673 #[serde(default)]
1674 pub status: Option<String>,
1675 /// YYYY-MM, by when it was sent.
1676 #[serde(default)]
1677 pub month: Option<String>,
1678}
1679
1680#[derive(Clone, Debug, Serialize, Deserialize)]
1681#[serde(rename_all = "camelCase")]
1682pub struct InvoiceSummary {
1683 pub invoice_id: String,
1684 /// `workspace` or `enterprise`.
1685 pub kind: String,
1686 /// The workspace's slug, or the enterprise's account id.
1687 pub account: String,
1688 /// What to call it: the workspace, or the enterprise's name.
1689 pub name: String,
1690 pub reason: String,
1691 pub period: String,
1692 pub amount_micros: i64,
1693 pub status: String,
1694 pub hosted_url: Option<String>,
1695 pub created_at: String,
1696 pub paid_at: Option<String>,
1697}
1698
1699/// `admin_audit`: every change made in sudo, and by Stripe, newest first.
1700/// Returns `Vec<AdminAction>`.
1701#[derive(Debug, Default, Serialize, Deserialize)]
1702pub struct AdminAuditArgs {
1703 #[serde(default)]
1704 pub by: Option<String>,
1705 #[serde(default)]
1706 pub action: Option<String>,
1707 /// Only those before this time, for paging.
1708 #[serde(default)]
1709 pub before: Option<String>,
1710}
1711
1712/// `admin_signals`: every workspace worth reaching out to, most urgent
1713/// first. Returns `Vec<Signal>`.
1714#[derive(Debug, Default, Serialize, Deserialize)]
1715pub struct AdminSignalsArgs {}
1716
1717/// What staff are doing about a workspace.
1718#[derive(Clone, Debug, Serialize, Deserialize)]
1719#[serde(rename_all = "camelCase")]
1720pub struct SalesRecord {
1721 pub workspace: String,
1722 /// `none`, `lead`, `contacted`, `negotiating`, `won`, `lost` or `churn_risk`.
1723 pub stage: String,
1724 /// The staff member looking after it.
1725 pub owner: Option<String>,
1726 pub next_step: Option<String>,
1727 /// RFC 3339 date.
1728 pub next_at: Option<String>,
1729 pub notes: Vec<SalesNote>,
1730 pub updated_at: Option<String>,
1731}
1732
1733#[derive(Clone, Debug, Serialize, Deserialize)]
1734#[serde(rename_all = "camelCase")]
1735pub struct SalesNote {
1736 pub id: String,
1737 pub text: String,
1738 pub by: String,
1739 pub created_at: String,
1740}
1741
1742/// `admin_sales`: a workspace's sales record. Returns `SalesRecord`.
1743#[derive(Debug, Serialize, Deserialize)]
1744pub struct AdminSalesArgs {
1745 pub workspace: String,
1746}
1747
1748/// `admin_set_sales`: its stage, owner and next step. Returns `Outcome<SalesRecord>`.
1749#[derive(Debug, Serialize, Deserialize)]
1750pub struct AdminSetSalesArgs {
1751 pub workspace: String,
1752 pub stage: String,
1753 #[serde(default)]
1754 pub owner: Option<String>,
1755 #[serde(default)]
1756 pub next_step: Option<String>,
1757 #[serde(default)]
1758 pub next_at: Option<String>,
1759 pub by: String,
1760}
1761
1762/// `admin_add_note`. Returns `Outcome<SalesRecord>`.
1763#[derive(Debug, Serialize, Deserialize)]
1764pub struct AdminAddNoteArgs {
1765 pub workspace: String,
1766 pub text: String,
1767 pub by: String,
1768}
1769
1770/// `admin_overview`: the business at a glance. Returns `Overview`.
1771#[derive(Debug, Default, Serialize, Deserialize)]
1772pub struct AdminOverviewArgs {}
1773
1774#[derive(Clone, Debug, Serialize, Deserialize)]
1775#[serde(rename_all = "camelCase")]
1776pub struct Overview {
1777 /// YYYY-MM.
1778 pub month: String,
1779 /// The last six months, oldest first, all workspaces together.
1780 pub months: Vec<MonthFigures>,
1781 /// This month by kind of usage: models, sandbox, deployments, plans.
1782 pub by_kind: Vec<KindFigures>,
1783 pub paying_workspaces: u32,
1784 pub stopped: u32,
1785 pub near_ceiling: u32,
1786 pub declined: u32,
1787 /// Sent and not yet paid, workspaces and enterprises.
1788 pub open_invoices_micros: i64,
1789 /// Follow-ups due today or earlier.
1790 pub follow_ups_due: u32,
1791 /// The capped budgets g1t pays from, this month.
1792 #[serde(default)]
1793 pub pools: Option<Pools>,
1794 /// This month's revenue: usage charged plus the plan's price paid.
1795 #[serde(default)]
1796 pub revenue_micros: i64,
1797 /// Workspaces on the paid plan now, and what their price comes to a
1798 /// month.
1799 #[serde(default)]
1800 pub active_plans: u32,
1801 #[serde(default)]
1802 pub plan_mrr_micros: i64,
1803 /// What g1t gave this month, by source, apart from its margin.
1804 #[serde(default)]
1805 pub given: Vec<GivenFigures>,
1806 /// g1t's own and Flagon's workspaces this month: what their use cost,
1807 /// and why they are not charged.
1808 #[serde(default)]
1809 pub internal: Vec<InternalUse>,
1810 /// Open limit requests, and workspaces in the Overages queue.
1811 #[serde(default)]
1812 pub open_requests: u32,
1813 #[serde(default)]
1814 pub overages: u32,
1815 /// Spend spikes waiting for an owner.
1816 #[serde(default)]
1817 pub open_spikes: u32,
1818}
1819
1820/// What g1t gave this month from one source.
1821#[derive(Clone, Debug, Default, PartialEq, Serialize, Deserialize)]
1822#[serde(rename_all = "camelCase")]
1823pub struct GivenFigures {
1824 /// `internal`, `trial`, `oss_pool`, `goodwill` or `covered`.
1825 pub source: String,
1826 pub label: String,
1827 /// At price, and what it cost g1t.
1828 pub micros: i64,
1829 pub cost_micros: i64,
1830}
1831
1832/// One internal workspace's use this month.
1833#[derive(Clone, Debug, Default, PartialEq, Serialize, Deserialize)]
1834#[serde(rename_all = "camelCase")]
1835pub struct InternalUse {
1836 pub workspace: String,
1837 /// Why it is not charged: its terms' note.
1838 pub reason: String,
1839 pub cost_micros: i64,
1840 pub entries: u32,
1841}
1842
1843/// g1t's capped budgets for free usage, this calendar month (UTC).
1844#[derive(Clone, Debug, Default, Serialize, Deserialize)]
1845#[serde(rename_all = "camelCase")]
1846pub struct Pools {
1847 /// YYYY-MM.
1848 pub month: String,
1849 /// Trial grants made this month, against the month's pool.
1850 pub trial_granted_micros: i64,
1851 pub trial_pool_micros: i64,
1852 pub trial_grants: u32,
1853 /// What the open-source pool paid this month, against its cap.
1854 pub oss_used_micros: i64,
1855 pub oss_pool_micros: i64,
1856 /// Each public repository's monthly cap on the pool.
1857 pub oss_repo_micros: i64,
1858}
1859
1860#[derive(Clone, Debug, Serialize, Deserialize)]
1861#[serde(rename_all = "camelCase")]
1862pub struct KindFigures {
1863 pub kind: String,
1864 pub charged_micros: i64,
1865 pub cost_micros: i64,
1866}
1867
1868// --- Staff (sudo.g1t.sh) ------------------------------------------------------
1869//
1870// Called only by the sudo app, which only g1t staff can reach (behind
1871// Cloudflare Access). Each change names who made it, and is kept in the
1872// audit log.
1873
1874/// `admin_accounts`: every billing account, with where each stands this
1875/// month. Returns `Vec<AccountSummary>`.
1876#[derive(Debug, Default, Serialize, Deserialize)]
1877pub struct AdminAccountsArgs {
1878 #[serde(default)]
1879 pub query: Option<String>,
1880 /// Exactly these workspaces' accounts, such as one page of sudo's
1881 /// list; every account with activity when absent.
1882 #[serde(default)]
1883 pub workspaces: Option<Vec<String>>,
1884}
1885
1886#[derive(Clone, Debug, Serialize, Deserialize)]
1887#[serde(rename_all = "camelCase")]
1888pub struct AccountSummary {
1889 pub account: BillingAccount,
1890 pub limit: Limit,
1891 /// Charged this month, after terms.
1892 pub charged_micros: i64,
1893 /// What this month's usage cost g1t.
1894 pub cost_micros: i64,
1895 /// Paid, ever.
1896 pub paid_micros: i64,
1897 /// The same figures for each of the account's workspaces that has
1898 /// any, so staff can see what one member of an enterprise used.
1899 #[serde(default)]
1900 pub by_workspace: Vec<WorkspaceFigures>,
1901 /// The last six months, oldest first, for trends.
1902 #[serde(default)]
1903 pub months: Vec<MonthFigures>,
1904}
1905
1906/// One month of an account's billing.
1907#[derive(Clone, Debug, Default, PartialEq, Serialize, Deserialize)]
1908#[serde(rename_all = "camelCase")]
1909pub struct MonthFigures {
1910 /// YYYY-MM.
1911 pub month: String,
1912 /// Usage charged, after what paid for it first.
1913 pub charged_micros: i64,
1914 /// What usage cost g1t: only what g1t paid for, never a workspace's own
1915 /// model provider.
1916 pub cost_micros: i64,
1917 pub paid_micros: i64,
1918 /// The plan's monthly price, paid.
1919 #[serde(default)]
1920 pub plans_micros: i64,
1921 /// What g1t gave, at price: internal (comped) use, trials, the
1922 /// open-source pool, goodwill credits and what g1t covered. Not margin.
1923 #[serde(default)]
1924 pub given_micros: i64,
1925}
1926
1927/// One workspace's share of an [`AccountSummary`].
1928#[derive(Clone, Debug, Default, PartialEq, Serialize, Deserialize)]
1929#[serde(rename_all = "camelCase")]
1930pub struct WorkspaceFigures {
1931 pub workspace: String,
1932 pub charged_micros: i64,
1933 pub cost_micros: i64,
1934 pub paid_micros: i64,
1935}
1936
1937/// `admin_account`: one account in full. Returns `Outcome<AccountDetail>`.
1938#[derive(Debug, Serialize, Deserialize)]
1939pub struct AdminAccountArgs {
1940 /// An account id, or a workspace slug.
1941 pub id: String,
1942}
1943
1944#[derive(Clone, Debug, Serialize, Deserialize)]
1945#[serde(rename_all = "camelCase")]
1946pub struct AccountDetail {
1947 pub summary: AccountSummary,
1948 /// Each workspace's limit, for an enterprise.
1949 pub workspaces: Vec<Limit>,
1950 pub ledger: Vec<LedgerEntry>,
1951 pub audit: Vec<AdminAction>,
1952}
1953
1954/// `admin_set_terms`. Returns `Outcome<BillingAccount>`.
1955#[derive(Debug, Serialize, Deserialize)]
1956pub struct AdminSetTermsArgs {
1957 pub id: String,
1958 pub terms: Terms,
1959 pub by: String,
1960}
1961
1962/// `admin_create_enterprise`. Returns `Outcome<BillingAccount>`.
1963#[derive(Debug, Serialize, Deserialize)]
1964pub struct AdminCreateEnterpriseArgs {
1965 pub name: String,
1966 pub workspaces: Vec<String>,
1967 pub by: String,
1968}
1969
1970/// `admin_attach`: moves a workspace onto an enterprise account, or back
1971/// onto its own with `account: None`. Returns `Outcome<BillingAccount>`.
1972#[derive(Debug, Serialize, Deserialize)]
1973pub struct AdminAttachArgs {
1974 pub workspace: String,
1975 pub account: Option<String>,
1976 pub by: String,
1977}
1978
1979/// `admin_credit`: money g1t gives a workspace, such as a refund or a
1980/// goodwill credit. Returns `Outcome<LedgerEntry>`.
1981#[derive(Debug, Serialize, Deserialize)]
1982pub struct AdminCreditArgs {
1983 pub workspace: String,
1984 pub amount_micros: i64,
1985 pub note: String,
1986 pub by: String,
1987}
1988
1989/// One change made in sudo.
1990#[derive(Clone, Debug, Serialize, Deserialize)]
1991#[serde(rename_all = "camelCase")]
1992pub struct AdminAction {
1993 pub id: String,
1994 pub account: String,
1995 pub action: String,
1996 pub detail: String,
1997 pub by: String,
1998 pub created_at: String,
1999}
2000
2001/// What a feature's plan costs and includes.
2002#[derive(Clone, Debug, Serialize, Deserialize)]
2003#[serde(rename_all = "camelCase")]
2004pub struct Plan {
2005 pub feature: Feature,
2006 pub title: String,
2007 /// Charged every month while the plan is on, in cents.
2008 pub monthly_cents: u32,
2009 /// What the monthly price includes, one line each, for people to read.
2010 pub includes: Vec<String>,
2011 /// How usage past the allowance is charged, for people to read.
2012 pub overage: String,
2013}
2014
2015#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize)]
2016#[serde(rename_all = "snake_case")]
2017pub enum SubscriptionStatus {
2018 /// Paid up; the feature works.
2019 Active,
2020 /// Paid up to the end of the period, and ends then.
2021 Canceling,
2022 /// The last payment failed; the feature is off until it is paid.
2023 PastDue,
2024 /// Ended.
2025 Canceled,
2026}
2027
2028impl SubscriptionStatus {
2029 /// Whether the feature works in this state.
2030 pub fn on(self) -> bool {
2031 matches!(self, SubscriptionStatus::Active | SubscriptionStatus::Canceling)
2032 }
2033}
2034
2035/// A workspace's plan for one feature.
2036#[derive(Clone, Debug, Serialize, Deserialize)]
2037#[serde(rename_all = "camelCase")]
2038pub struct Subscription {
2039 pub feature: Feature,
2040 pub status: SubscriptionStatus,
2041 /// RFC 3339: when the period paid for ends, and the plan renews or
2042 /// ends.
2043 pub period_end: Option<String>,
2044 /// Username of whoever turned it on.
2045 pub started_by: String,
2046 /// RFC 3339.
2047 pub started_at: String,
2048}
2049
2050/// A feature as a workspace sees it: what it costs, and its plan if it has
2051/// one.
2052#[derive(Clone, Debug, Serialize, Deserialize)]
2053#[serde(rename_all = "camelCase")]
2054pub struct FeatureState {
2055 pub plan: Plan,
2056 pub subscription: Option<Subscription>,
2057 /// Whether the feature works for the workspace now.
2058 pub on: bool,
2059 /// On without a plan: comped terms, or given by g1t. Nothing to pay
2060 /// and nothing to turn off.
2061 #[serde(default)]
2062 pub included: bool,
2063}
2064
2065/// `features`: every paid feature and the workspace's plan for each.
2066/// Members only. Returns `Outcome<Vec<FeatureState>>`.
2067#[derive(Debug, Serialize, Deserialize)]
2068pub struct FeaturesArgs {
2069 pub workspace: String,
2070 pub viewer: Viewer,
2071}
2072
2073/// `subscribe`: starts the card page for a feature's monthly plan. Owners
2074/// only. Returns `Outcome<Checkout>`; the page's id comes back to
2075/// `return_url` as `session`, for `confirm_subscription`.
2076#[derive(Debug, Serialize, Deserialize)]
2077#[serde(rename_all = "camelCase")]
2078pub struct SubscribeArgs {
2079 pub actor: User,
2080 pub workspace: String,
2081 pub feature: Feature,
2082 pub return_url: String,
2083}
2084
2085/// `confirm_subscription`: turns the feature on once the processor says
2086/// the plan was paid for. Safe to call any number of times. Returns
2087/// `Outcome<FeatureState>`.
2088#[derive(Debug, Serialize, Deserialize)]
2089pub struct ConfirmSubscriptionArgs {
2090 pub workspace: String,
2091 pub viewer: Viewer,
2092 pub session: String,
2093}
2094
2095/// `close_workspace`: settles a workspace that is about to be deleted.
2096/// Owners only. Refused while it has an invoice that failed, while it
2097/// holds prepaid credit, or while it owes money it cannot be charged for
2098/// now; otherwise what it owes is invoiced to its card at once (no
2099/// minimum), its plan is cancelled at Stripe straight away, and its
2100/// account is marked closed, so the month-end close, autopay and limit
2101/// warnings pass it by. Its ledger, invoices and statements stay. With
2102/// `dry_run`, only says whether it could, changing nothing. Returns
2103/// `Outcome<bool>`.
2104#[derive(Debug, Serialize, Deserialize)]
2105#[serde(rename_all = "camelCase")]
2106pub struct CloseWorkspaceArgs {
2107 pub actor: User,
2108 pub workspace: String,
2109 #[serde(default)]
2110 pub dry_run: bool,
2111}
2112
2113/// `cancel_subscription` (`resume` false) ends a plan at the end of the
2114/// period paid for; with `resume` true, takes that back. Owners only.
2115/// Returns `Outcome<FeatureState>`.
2116#[derive(Debug, Serialize, Deserialize)]
2117pub struct CancelSubscriptionArgs {
2118 pub actor: User,
2119 pub workspace: String,
2120 pub feature: Feature,
2121 #[serde(default)]
2122 pub resume: bool,
2123}
2124
2125/// `has_feature`: whether a feature works for a workspace now, asked by the
2126/// service that provides it before doing paid work. Returns
2127/// `Outcome<bool>`: a failure, with the reason to show, when it does not.
2128/// True everywhere when no card processor is configured.
2129#[derive(Debug, Serialize, Deserialize)]
2130pub struct HasFeatureArgs {
2131 pub workspace: String,
2132 pub feature: Feature,
2133}
2134
2135/// `charge_feature`: usage of a feature past its plan's allowance, charged
2136/// from the workspace's credit at cost plus the margin, whatever
2137/// `FREE_WHILE_BUILDING` says. Called by the service that provides it.
2138/// Charged once per `reference`. Returns `Outcome<bool>`: false if that
2139/// reference was charged before.
2140#[derive(Debug, Serialize, Deserialize)]
2141#[serde(rename_all = "camelCase")]
2142pub struct ChargeFeatureArgs {
2143 pub workspace: String,
2144 pub feature: Feature,
2145 /// What it cost g1t, in millionths of a dollar, before the margin.
2146 pub cost_micros: i64,
2147 pub description: String,
2148 /// `namespace/name`, when the usage was one repository's.
2149 pub repo: Option<String>,
2150 /// Unique to this charge, e.g. `deployments/acme/2026-10`.
2151 pub reference: String,
2152 /// For a build: how long it ran. The plan's included build time this
2153 /// month pays for what it can, and only the rest of `cost_micros` is
2154 /// charged.
2155 #[serde(default)]
2156 pub build_seconds: Option<u32>,
2157}
2158
2159#[cfg(test)]
2160mod tests {
2161 use super::*;
2162
2163 #[test]
2164 fn an_account_carries_no_run_fee() {
2165 let account = Account {
2166 workspace: "acme".into(),
2167 balance_micros: 0,
2168 status: Status { enabled: true, live: false, free: false },
2169 margin_percent: 20,
2170 card: None,
2171 };
2172 let json = serde_json::to_value(account).unwrap();
2173 let mut keys: Vec<&str> = json.as_object().unwrap().keys().map(String::as_str).collect();
2174 keys.sort_unstable();
2175 assert_eq!(keys, ["balanceMicros", "card", "marginPercent", "status", "workspace"]);
2176 }
2177
2178 #[test]
2179 fn a_price_change_says_when_the_markup_moved() {
2180 let change = PriceChange {
2181 meter: "sandbox_second".into(),
2182 old_cost_micros: 21.0,
2183 new_cost_micros: 21.0,
2184 markup_percent: 20,
2185 old_markup_percent: Some(138),
2186 reason: "Sandbox time is now charged at cost plus 20% from the first second".into(),
2187 created_at: "2026-10-05T00:00:00Z".into(),
2188 };
2189 assert_eq!(serde_json::to_value(&change).unwrap()["oldMarkupPercent"], 138);
2190 let cost_only = PriceChange { old_markup_percent: None, ..change };
2191 assert!(serde_json::to_value(&cost_only).unwrap().get("oldMarkupPercent").is_none());
2192 }
2193
2194 #[test]
2195 fn features_are_named_as_the_site_sends_them() {
2196 assert_eq!(
2197 serde_json::to_value(Feature::Deployments).unwrap(),
2198 serde_json::json!("deployments")
2199 );
2200 assert_eq!(Feature::parse("deployments"), Some(Feature::Deployments));
2201 assert_eq!(serde_json::to_value(Feature::Plan).unwrap(), serde_json::json!("plan"));
2202 assert_eq!(Feature::parse("plan"), Some(Feature::Plan));
2203 // Older readers named the plan Team.
2204 assert_eq!(Feature::parse("team"), Some(Feature::Plan));
2205 assert_eq!(serde_json::from_value::<Feature>(serde_json::json!("team")).unwrap(), Feature::Plan);
2206 assert_eq!(Feature::ALL, [Feature::Plan]);
2207 assert!(SubscriptionStatus::Canceling.on());
2208 assert!(!SubscriptionStatus::PastDue.on());
2209 }
2210
2211 #[test]
2212 fn a_reservation_is_asked_for_and_answered_in_camel_case() {
2213 let asked: ReserveArgs = serde_json::from_value(serde_json::json!({
2214 "workspace": "acme",
2215 "repo": { "namespace": "acme", "name": "web" },
2216 "public": true,
2217 "kind": "check",
2218 "estimateMicros": 2_000_000,
2219 }))
2220 .unwrap();
2221 assert_eq!(asked.kind, ComputeKind::Check);
2222 assert!(asked.kind.open_source_pool());
2223 assert!(!ComputeKind::Agent.open_source_pool());
2224 // Rust callers that write snake_case are read too.
2225 let snake: ReserveArgs = serde_json::from_value(serde_json::json!({
2226 "workspace": "acme",
2227 "repo": { "namespace": "acme", "name": "web" },
2228 "public": false,
2229 "kind": "agent",
2230 "estimate_micros": 1,
2231 }))
2232 .unwrap();
2233 assert_eq!(snake.estimate_micros, 1);
2234 let answer = Reservation { id: "rsv_1".into(), paid_by: PaidBy::OnDemand, held_micros: 5, expires_at: String::new() };
2235 assert_eq!(serde_json::to_value(&answer).unwrap()["paidBy"], "on_demand");
2236 assert_eq!(serde_json::to_value(PlanKind::Internal).unwrap(), "internal");
2237 assert!(!PlanKind::Free.on_demand() && PlanKind::Enterprise.on_demand());
2238 }
2239
2240 #[test]
2241 fn a_refusal_carries_its_own_code() {
2242 let refused: crate::Outcome<Reservation> =
2243 crate::Outcome::fail(crate::FailureCode::OssPoolEmpty, "The open-source pool is spent.");
2244 let json = serde_json::to_value(&refused).unwrap();
2245 assert_eq!(json["error"]["code"], "oss_pool_empty");
2246 assert_eq!(crate::FailureCode::NotPaid.http_status(), 402);
2247 assert_eq!(crate::FailureCode::Paused.http_status(), 409);
2248 }
2249
2250 #[test]
2251 fn who_pays_is_read_as_the_runner_sends_it() {
2252 let run: StartRunArgs = serde_json::from_value(serde_json::json!({
2253 "workspace": "acme",
2254 "repo": { "namespace": "acme", "name": "web" },
2255 "number": 7,
2256 "task": "implement",
2257 "model": "Claude Sonnet 5.5",
2258 "billedTo": "workspace",
2259 }))
2260 .unwrap();
2261 assert_eq!(run.billed_to, "workspace");
2262 }
2263}