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