pr_01m47d24b0e6n91zwymwxg0vpx/packages/contracts/src/billing.ts

935 lines38,099 bytesCodeBlame
1import type { ComputeKind, Reservation } from "./compute";
2import type { User, Viewer } from "./identity";
3import type { RepoPath } from "./repos";
4import type { Result } from "./result";
5
6/** Millionths of a US dollar in one dollar: the unit money is held in. */
7export const MICROS_PER_DOLLAR = 1_000_000;
8
9/** Whether workspaces are charged for agents at all, and with real money. */
10export type BillingStatus = {
11 /**
12 * False when no card processor is configured: nothing is charged, and who
13 * may run agents is decided some other way.
14 */
15 enabled: boolean;
16 /** False while the card processor is in its test mode, where cards are not real. */
17 live: boolean;
18 /**
19 * True while g1t is being built out: runs are recorded with what they
20 * cost, but nothing is charged and no credit is needed. Not forever.
21 */
22 free?: boolean;
23};
24
25/** A workspace's standing. */
26export type BillingAccount = {
27 workspace: string;
28 /**
29 * Credit left, in millionths of a dollar. Can dip below zero by the cost
30 * of the runs that were under way when it ran out.
31 */
32 balanceMicros: number;
33 status: BillingStatus;
34 /** What is added to a run's cost, in percent. */
35 marginPercent: number;
36 /** The card charged near the limit and when a month closes, if one is saved. */
37 card?: { brand: string; last4: string; expMonth: number; expYear: number } | null;
38};
39
40/** One line of a workspace's statement. */
41export type LedgerEntry = {
42 id: string;
43 /** Credit bought with a card, or an agent's run. */
44 kind: "top_up" | "usage";
45 /** Positive for credit added, negative for usage. */
46 amountMicros: number;
47 description: string;
48 /** For usage: the repository and pull request the agent worked on. */
49 repo: string | null;
50 number: number | null;
51 /** For usage: `implement`, `review` or `update`. */
52 task: string | null;
53 /** For usage: the model, by its public name. */
54 model: string | null;
55 /** For usage: who paid the model provider. */
56 billedTo: "g1t" | "workspace";
57 /** For a top-up: the username of whoever paid. */
58 createdBy: string | null;
59 /** RFC 3339. */
60 createdAt: string;
61 /** The workspace the line belongs to, which tells an enterprise's lines apart. */
62 workspace?: string | null;
63 /** For usage: what the plan's included usage paid of it. `amountMicros` is what is left to pay. */
64 creditMicros?: number;
65 /** For usage: what the workspace's trial credit paid of it. */
66 trialMicros?: number;
67 /** For usage: what g1t's open-source pool paid of it. */
68 ossMicros?: number;
69 /** For usage: what g1t covered itself, such as a trial's last run past its credit. */
70 givenMicros?: number;
71};
72
73/** What lets a sandbox, and nothing else, report what its run cost. */
74export type RunTicket = { runId: string; token: string };
75
76/**
77 * What agents cost, charged to the workspace they worked for. A workspace
78 * buys credit; each run deducts its cost plus g1t's margin; with no credit,
79 * no agent starts.
80 */
81/**
82 * The free allowance on g1t's hosted models for a workspace not otherwise
83 * open to them: a few dollars of model cost each, out of one pool, until a
84 * date. Mirrors `Trial` in `crates/contracts/src/billing.rs`.
85 */
86/** How an account is charged. Standard unless g1t set otherwise in sudo. */
87export type Terms = {
88 kind: "standard" | "comped" | "custom";
89 discountPercent: number;
90 ceilingMicros: number | null;
91 note: string;
92 until: string | null;
93 setBy: string | null;
94 setAt: string | null;
95};
96
97/**
98 * Who pays: a workspace's own account, or an enterprise's, which pays for
99 * several workspaces with one bill and one limit.
100 */
101export type PayingAccount = {
102 id: string;
103 kind: "workspace" | "enterprise";
104 name: string;
105 terms: Terms;
106 workspaces: string[];
107 /** Where an enterprise's invoices go. */
108 billingEmail?: string | null;
109 /** An enterprise's invoices, newest first. */
110 invoices?: EnterpriseInvoice[];
111 createdAt: string;
112 /** What g1t staff set for the account beyond its terms. */
113 allowances?: Allowances;
114};
115
116/** Set per account by g1t staff in sudo, on top of its terms. */
117export type Allowances = {
118 /** The g1t plan without its monthly price; usage is charged as usual. Comped accounts have it anyway. */
119 plan: boolean;
120 /** Each public repository's monthly cap on g1t's open-source pool; null for the default. */
121 ossRepoMicros: number | null;
122 /** Each workspace's trial credit, outside the monthly pool; null for the default. */
123 trialMicros: number | null;
124 /** Agents at once, in place of the plan's (2 in the first month or on the trial, then 10); null for the default. */
125 maxConcurrentAgents?: number | null;
126 /** One run's spend cap, in place of the owners' and the default $2; null for none. */
127 runCapMicros?: number | null;
128 /** What one issue's agents may spend in all, in place of the owners' and the default $10; null for none. */
129 issueCapMicros?: number | null;
130 /** A hold on new compute, with why; null for none. */
131 hold?: string | null;
132};
133
134// --- Entitlements, and compute started under a reservation ------------------
135//
136// Every service that starts compute asks billing first:
137// 1. `entitlements(workspace)`: what it may do at all, its caps, and whether compute is paused.
138// 2. `reserve(...)`: holds the estimate against what may pay for it, so starts at the same moment
139// cannot overshoot together. Answers who pays first, or refuses with a stable code
140// (`not_paid`, `trial_used`, `limit`, `paused`, `oss_pool_empty`) and a message for the owner.
141// 3. `settle(reservationId, actualMicros)`: releases the hold. The charge goes on the ledger the usual way.
142// A reservation never settled lapses after `RESERVATION_HOURS`.
143
144/** A reservation that is never settled stops holding after this long. */
145export const RESERVATION_HOURS = 3;
146/** What a ceiling reads as when there is none (g1t's own workspaces). */
147export const UNLIMITED_MICROS = 1_000_000_000_000_000;
148
149/** What a workspace pays g1t on, as far as compute is concerned. Mirrors `PlanKind`. */
150export type PlanKind = "free" | "paid" | "internal" | "enterprise";
151
152// `ComputeKind` (what compute is for), `PaidBy` (who pays first: credit, trial, oss, on_demand) and
153// `Reservation` are in `./compute`, with the gate that calls `reserve` and `settle`.
154
155/** One level reached: 50, 75, 90 or 100 percent. */
156export type UsageAlert = {
157 /** `included` (the plan's included usage), `spend_limit` or `ceiling`. */
158 meter: "included" | "spend_limit" | "ceiling" | string;
159 level: number;
160 usedMicros: number;
161 limitMicros: number;
162 message: string;
163};
164
165/** An hour's spend well above the workspace's usual: new compute waits for an owner. */
166export type Spike = {
167 id: string;
168 /** `open` (waiting), `continued` (keep going) or `stopped`. */
169 status: "open" | "continued" | "stopped" | string;
170 hourMicros: number;
171 averageMicros: number;
172 detectedAt: string;
173 decidedBy?: string | null;
174 decidedAt?: string | null;
175 /** While continued: until when, unless spend doubles again first. */
176 until?: string | null;
177};
178
179/** What a workspace may do now. Mirrors `Entitlements` in `crates/contracts/src/billing.rs`. */
180export type Entitlements = {
181 workspace: string;
182 plan: PlanKind;
183 /** May start sandboxes, models, deployments and semantic search at all: paid, internal, enterprise, or free with trial credit left. */
184 compute: boolean;
185 /** The one-time trial credit left; 0 if none or used. */
186 trialMicrosLeft: number;
187 /** A card check has been done; the trial and the open-source pool need it. */
188 trialVerified: boolean;
189 /** A paid workspace still in its first billing cycle. */
190 firstMonth: boolean;
191 /** 2 in the first month or on the trial, 10 after; staff can override it. */
192 maxConcurrentAgents: number;
193 /** 60 in the first month or on the trial; otherwise the guardrails' own caps. */
194 maxRunMinutes: number;
195 /** One run's spend cap, $2 by default; staff can override it. */
196 runCapMicros: number;
197 /** Agent spend on one issue in all, $10 by default. */
198 issueCapMicros: number;
199 /** g1t's ceiling on usage not yet paid for; `UNLIMITED_MICROS` for g1t's own; 0 for free. */
200 ceilingMicros: number;
201 /** Usage not yet paid for this month, prepayment taken off. */
202 exposureMicros: number;
203 /** Why new compute is paused, for the owner; null when it is not. */
204 paused: string | null;
205 /** What open reservations hold now. */
206 heldMicros?: number;
207 /** Paid in advance and not used yet. */
208 prepaidMicros?: number;
209 /** The plan's included usage each month, and what of it is used. */
210 includedMicros?: number;
211 includedUsedMicros?: number;
212 /** How far back the audit log can be read and exported: the same on every plan. */
213 auditRetentionDays: number;
214 /** Private repository storage included before it is charged. */
215 freePrivateStorageBytes: number;
216 /** The last daily measure of the workspace's private repositories (a lower bound). */
217 privateStorageBytes: number;
218 /** What g1t's open-source pool paid for the workspace this month. */
219 ossPaidMicros: number;
220 /** Build time the plan includes each month, and used. */
221 buildSecondsIncluded: number;
222 buildSecondsUsed: number;
223 /** Git operations this month, and how many are included. */
224 gitOperations?: number;
225 gitOperationsIncluded?: number;
226 /** The smallest amount a card is charged when a month closes. */
227 minChargeMicros: number;
228 /** A spend spike waiting for an owner, or decided. */
229 spike?: Spike | null;
230 /** Where usage stands against what is included and the limits, from 50%. */
231 alerts?: UsageAlert[];
232};
233
234/** A hold on a start's estimated cost, as billing answers it (`Reservation` in `./compute`, and more). */
235export type ReservationHeld = Reservation & {
236 /** What is held, at cost; may be less than the estimate for a free workspace's last bit of trial. */
237 heldMicros?: number;
238 /** When the hold lapses if never settled. */
239 expiresAt?: string;
240};
241
242/** A request to g1t: a higher limit, or help with usage past what was meant. */
243export type LimitRequest = {
244 id: string;
245 workspace: string;
246 kind: "limit" | "overage" | string;
247 amountMicros: number;
248 reason: string;
249 expectedMonthlyMicros: number;
250 status: "open" | "approved" | "declined" | string;
251 decidedMicros?: number | null;
252 decidedBy?: string | null;
253 /** The answer, as the owner sees it. */
254 answer?: string | null;
255 createdBy: string;
256 createdAt: string;
257 decidedAt?: string | null;
258};
259
260/** What staff see beside a request. */
261export type WorkspaceHistory = {
262 plan: PlanKind | null;
263 months: MonthFigures[];
264 paidClearedMicros: number;
265 payments: number;
266 disputes: number;
267 declines: number;
268 firstSeen: string | null;
269 ceilingMicros: number | null;
270 maxCeilingMicros: number | null;
271 spendLimitMicros: number | null;
272 lastHourMicros: number;
273 averageHourMicros: number;
274 lastDayMicros: number;
275};
276
277export type LimitRequestReview = { request: LimitRequest; history: WorkspaceHistory };
278
279/** What a one-time goodwill credit comes to: the margin on the overage, always, plus its cost up to the cap. */
280export type Goodwill = {
281 overageMicros: number;
282 marginMicros: number;
283 costMicros: number;
284 creditMicros: number;
285 absorbedMicros: number;
286};
287
288/** A workspace whose month went well past its usual, or hit a spike. */
289export type Overage = {
290 workspace: string;
291 plan: PlanKind;
292 typicalMonthMicros: number;
293 thisMonthMicros: number;
294 costMicros: number;
295 marginMicros: number;
296 spike: Spike | null;
297 topEntries: LedgerEntry[];
298 goodwill: Goodwill;
299 goodwillAvailable: boolean;
300 lastGoodwillAt: string | null;
301 request: LimitRequest | null;
302};
303
304/** One workspace's recent pace. */
305export type Velocity = {
306 workspace: string;
307 plan: PlanKind;
308 lastHourMicros: number;
309 averageHourMicros: number;
310 lastDayMicros: number;
311 thisMonthMicros: number;
312 ratio: number;
313 spike: Spike | null;
314 firstSeen: string | null;
315};
316
317/** g1t's capped budgets for free usage this month. */
318export type Pools = {
319 month: string;
320 trialGrantedMicros: number;
321 trialPoolMicros: number;
322 trialGrants: number;
323 ossUsedMicros: number;
324 ossPoolMicros: number;
325 ossRepoMicros: number;
326};
327
328export type AccountSummary = {
329 account: PayingAccount;
330 limit: Limit;
331 chargedMicros: number;
332 costMicros: number;
333 paidMicros: number;
334 /** The same figures for each of the account's workspaces that has any. */
335 byWorkspace: WorkspaceFigures[];
336 /** The last six months, oldest first. */
337 months?: MonthFigures[];
338};
339
340/** One workspace's share of an `AccountSummary`. */
341export type WorkspaceFigures = { workspace: string; chargedMicros: number; costMicros: number; paidMicros: number };
342
343export type StripeStatus = {
344 /** `test` or `live`, from the key; `off` without one. */
345 mode: "test" | "live" | "off" | string;
346 webhook: { url: string; endpointId: string; events: string[]; createdBy: string; createdAt: string } | null;
347 recentEvents: { id: string; kind: string; outcome: string; receivedAt: string }[];
348 error: string | null;
349};
350
351/** An enterprise's invoice: one line per workspace, paid on Stripe's page. */
352export type EnterpriseInvoice = {
353 invoiceId: string;
354 hostedUrl: string | null;
355 amountMicros: number;
356 status: "open" | "paid" | "overdue" | "void" | string;
357 period: string;
358 lines: { workspace: string; amountMicros: number }[];
359 createdAt: string;
360};
361
362/** A workspace's invoice: monthly, or when charged near its limit. Itemised, in Stripe's billing page. */
363export type WorkspaceInvoice = {
364 invoiceId: string;
365 workspace: string;
366 reason: "month" | "threshold" | string;
367 period: string;
368 amountMicros: number;
369 status: "paid" | "open" | "failed" | "void" | string;
370 hostedUrl: string | null;
371 pdfUrl: string | null;
372 lines: { description: string; amountMicros: number }[];
373 createdAt: string;
374};
375
376/** A month of the ledger, grouped by day or project, a line per kind of charge. */
377export type Statement = {
378 month: string;
379 months: string[];
380 groups: {
381 key: string;
382 label: string;
383 /** `coveredMicros`: what the plan's included usage, the trial, the open-source pool or g1t paid, not in `chargedMicros`. */
384 lines: { kind: string; count: number; chargedMicros: number; costMicros: number; coveredMicros?: number }[];
385 chargedMicros: number;
386 }[];
387 totals: {
388 chargedMicros: number;
389 paidMicros: number;
390 costMicros: number;
391 entries: number;
392 /** What paid for usage before it was charged, such as "Paid by g1t's open-source pool". */
393 covered?: { source: "included" | "trial" | "oss_pool" | "given" | string; label: string; micros: number }[];
394 /** Owed when the month closed but under the minimum charge: on the next invoice. */
395 carriedMicros?: number;
396 };
397};
398
399export type MonthFigures = {
400 month: string;
401 /** Usage charged, after what paid for it first. */
402 chargedMicros: number;
403 /** What usage cost g1t: never a workspace's own model provider. */
404 costMicros: number;
405 paidMicros: number;
406 /** The plan's monthly price, paid. */
407 plansMicros?: number;
408 /** What g1t gave at price (internal use, trials, the open-source pool, goodwill, covered). Not margin. */
409 givenMicros?: number;
410};
411
412/** What g1t gave this month from one source. */
413export type GivenFigures = { source: "internal" | "trial" | "oss_pool" | "goodwill" | "covered" | string; label: string; micros: number; costMicros: number };
414
415/** One internal workspace's use this month, and why it is not charged. */
416export type InternalUse = { workspace: string; reason: string; costMicros: number; entries: number };
417
418export type SignalKind = "at_limit" | "near_ceiling" | "declined" | "growing" | "established" | "first_payment" | "high_spend";
419
420/** Why a workspace is worth reaching out to. */
421export type Signal = {
422 workspace: string;
423 kind: SignalKind;
424 detail: string;
425 valueMicros: number;
426 stage: string | null;
427 owner: string | null;
428 nextStep?: string | null;
429 /** When the next step is due, YYYY-MM-DD. */
430 nextAt?: string | null;
431};
432
433/** One invoice g1t has sent, a workspace's or an enterprise's. */
434export type InvoiceSummary = {
435 invoiceId: string;
436 kind: "workspace" | "enterprise";
437 account: string;
438 name: string;
439 reason: string;
440 period: string;
441 amountMicros: number;
442 status: string;
443 hostedUrl: string | null;
444 createdAt: string;
445 paidAt: string | null;
446};
447
448export type SalesStage = "none" | "lead" | "contacted" | "negotiating" | "won" | "lost" | "churn_risk";
449
450export type SalesRecord = {
451 workspace: string;
452 stage: SalesStage | string;
453 owner: string | null;
454 nextStep: string | null;
455 nextAt: string | null;
456 notes: { id: string; text: string; by: string; createdAt: string }[];
457 updatedAt: string | null;
458};
459
460export type Overview = {
461 month: string;
462 months: MonthFigures[];
463 byKind: { kind: string; chargedMicros: number; costMicros: number }[];
464 payingWorkspaces: number;
465 stopped: number;
466 nearCeiling: number;
467 declined: number;
468 openInvoicesMicros: number;
469 followUpsDue: number;
470 /** g1t's capped budgets for free usage, this month. */
471 pools?: Pools | null;
472 /** Usage charged plus the plan's price paid, this month. */
473 revenueMicros?: number;
474 activePlans?: number;
475 planMrrMicros?: number;
476 /** What g1t gave this month, by source, apart from its margin. */
477 given?: GivenFigures[];
478 /** g1t's own and Flagon's workspaces: what their use cost, and why they are not charged. */
479 internal?: InternalUse[];
480 openRequests?: number;
481 overages?: number;
482 openSpikes?: number;
483};
484
485/** A customer's Stripe billing page, for staff to send them. */
486export type BillingLink = {
487 /** One-time and short-lived, signed in already. */
488 portalUrl: string;
489 /** The page's sign-in, which does not expire: the customer signs in by email. */
490 loginUrl: string | null;
491 customerEmail: string | null;
492 expiresNote: string;
493};
494
495export type AdminAction = { id: string; account: string; action: string; detail: string; by: string; createdAt: string };
496
497export type AccountDetail = {
498 summary: AccountSummary;
499 workspaces: Limit[];
500 ledger: LedgerEntry[];
501 audit: AdminAction[];
502};
503
504/** Staff-only billing, for sudo.g1t.sh. Every change names who made it. */
505export interface BillingAdminApi {
506 accounts(query?: string): Promise<AccountSummary[]>;
507 account(id: string): Promise<Result<AccountDetail>>;
508 setTerms(id: string, terms: Terms, by: string): Promise<Result<PayingAccount>>;
509 /** Team on or off without charge, and the account's share of the pools. Needs a note. */
510 setAllowances(id: string, allowances: Allowances, note: string, by: string): Promise<Result<PayingAccount>>;
511 createEnterprise(name: string, workspaces: string[], by: string): Promise<Result<PayingAccount>>;
512 attach(workspace: string, account: string | null, by: string): Promise<Result<PayingAccount>>;
513 credit(workspace: string, amountMicros: number, note: string, by: string): Promise<Result<LedgerEntry>>;
514 /** The workspace's Stripe billing page, to send to the customer. Logged. */
515 billingLink(workspace: string, by: string): Promise<Result<BillingLink>>;
516 /** Where billing stands with Stripe; with `setup`, registers the webhook first. */
517 stripe(setup?: boolean, by?: string): Promise<StripeStatus>;
518 /** Where an enterprise's invoices go; makes its Stripe customer. */
519 enterpriseBilling(id: string, email: string, by: string): Promise<Result<PayingAccount>>;
520 /** Sends an enterprise its invoice now, for what its workspaces owe. */
521 invoiceEnterprise(id: string, by: string): Promise<Result<EnterpriseInvoice>>;
522 /** Exactly these workspaces' accounts, such as one page of the list. */
523 accountsFor(workspaces: string[]): Promise<AccountSummary[]>;
524 /** Every workspace worth reaching out to, most urgent first. */
525 signals(): Promise<Signal[]>;
526 /** The business at a glance. */
527 overview(): Promise<Overview>;
528 /** A workspace's sales record. */
529 sales(workspace: string): Promise<SalesRecord>;
530 setSales(workspace: string, record: { stage: string; owner?: string | null; nextStep?: string | null; nextAt?: string | null }, by: string): Promise<Result<SalesRecord>>;
531 addNote(workspace: string, text: string, by: string): Promise<Result<SalesRecord>>;
532 /** A workspace's invoices from g1t, for staff. */
533 workspaceInvoices(workspace: string): Promise<WorkspaceInvoice[]>;
534 /** Every invoice g1t has sent, newest first. */
535 allInvoices(filter?: { status?: string; month?: string }): Promise<InvoiceSummary[]>;
536 /** Every change made in sudo and by Stripe, newest first, 100 at a time. */
537 audit(filter?: { by?: string; action?: string; before?: string }): Promise<AdminAction[]>;
538 /** Limit and overage requests, with each workspace's history; `open` by default. */
539 limitRequests(status?: "open" | "approved" | "declined" | "all"): Promise<LimitRequestReview[]>;
540 /** Approve (at the amount asked, or another) or decline; the owner is told in the app and by email. */
541 decideLimitRequest(id: string, decision: "approve" | "decline", amountMicros: number | null, note: string, by: string): Promise<Result<LimitRequest>>;
542 /** The Overages queue. */
543 overages(): Promise<Overage[]>;
544 /** A goodwill credit; no amount is the one-click credit. Larger, or a second in 12 months, needs a reason. */
545 goodwill(workspace: string, amountMicros: number | null, reason: string, by: string, day?: string | null): Promise<Result<LedgerEntry>>;
546 /** Workspaces spending in the last day, fastest first. */
547 velocity(): Promise<Velocity[]>;
548 /** Money that reached g1t outside the card pages, such as a bank transfer: entered as a payment. */
549 recordPayment(workspace: string, amountMicros: number, reference: string, note: string, by: string): Promise<Result<LedgerEntry>>;
550}
551
552/** How much a workspace has earned g1t's trust with money. */
553export type Trust = "new" | "paid" | "established" | "reviewed" | "internal";
554
555/**
556 * How far a workspace's unpaid usage has gone this month, and where its
557 * work stops: past `ceilingMicros`, no new sandboxes, builds or app
558 * requests. Usage counts at its cost to g1t or its charge, whichever is
559 * more, so it counts while g1t is free too.
560 */
561export type Limit = {
562 workspace: string;
563 /** The account that pays: the workspace's own (`ws_<slug>`), or its enterprise's. */
564 account: string;
565 accountName: string;
566 trust: Trust;
567 exposureMicros: number;
568 /** The lower of g1t's ceiling and the owner's spend limit; null for g1t's own. */
569 ceilingMicros: number | null;
570 trustCeilingMicros: number | null;
571 spendLimitMicros: number | null;
572 state: "ok" | "warning" | "stopped";
573 message: string | null;
574 /** Charged this month: what the spend limit is measured against. */
575 spentMicros?: number;
576 /** True while the owners have not chosen a limit, so the automatic one applies: $200, or twice last month's spend. */
577 defaultSpendLimit?: boolean;
578 /** The most owners may set their own limit to without asking: the highest ceiling ever, plus what is prepaid. */
579 availableMicros?: number | null;
580 /** How the ceiling grows from here, in a sentence. */
581 growth?: string | null;
582 /** Paid in advance and not used yet; raises what can be used before work stops by as much. */
583 prepaidMicros?: number;
584 /** The highest ceiling the workspace has had. */
585 maxCeilingMicros?: number | null;
586 /** The most owners may raise the limit to themselves, once: twice the highest ceiling. Null once used. */
587 raiseOnceMicros?: number | null;
588 /** When the one-time raise was used. */
589 raisedAt?: string | null;
590 /** A paid workspace's first billing cycle, on the starting ceiling. */
591 firstMonth?: boolean;
592};
593
594/** One metered unit: what it costs g1t and what it is sold at; the price follows the cost. */
595export type Price = {
596 meter:
597 | "sandbox_second"
598 | "build_second"
599 | "app_requests"
600 | "app_cpu"
601 | "app_month"
602 | "custom_domain_month"
603 | "private_storage"
604 | "embedding_tokens"
605 | "scan_cpu"
606 | "scan_rows"
607 | string;
608 title: string;
609 unit: string;
610 costMicros: number;
611 markupPercent: number;
612 priceMicros: number;
613 /** `list`: Cloudflare's published price. `cloudflare`: measured from Cloudflare's bill. */
614 source: "list" | "cloudflare" | string;
615 checkedAt: string | null;
616 updatedAt: string;
617};
618
619export type PriceChange = {
620 meter: string;
621 oldCostMicros: number;
622 newCostMicros: number;
623 markupPercent: number;
624 /** The markup before, when the change was to the markup rather than the cost. */
625 oldMarkupPercent?: number;
626 reason: string;
627 createdAt: string;
628};
629
630export type PriceBook = {
631 prices: Price[];
632 changes: PriceChange[];
633 modelMarginPercent: number;
634 /** Every plan, as sold now. */
635 plans?: FeaturePlan[];
636 /** What is free, and the capped budgets that pay for it. */
637 free?: FreeTier | null;
638};
639
640/** What g1t gives without a plan; each is paid for by a capped budget. */
641export type FreeTier = {
642 /** Each new workspace's trial credit, once. */
643 trialWorkspaceMicros: number;
644 /** Trial grants each month, in all; new trials wait when it is spent. */
645 trialMonthlyPoolMicros: number;
646 /** g1t's open-source pool each month, and any one repository's share. */
647 ossPoolMicros: number;
648 ossRepoMicros: number;
649 /** Private repository storage before it is charged. */
650 freePrivateStorageBytes: number;
651 /** Days of audit log, the same on every plan. */
652 auditRetentionDays: number;
653 /** The smallest amount a card is charged when a month closes; less carries over. */
654 minChargeMicros: number;
655 /** Git operations included each month on every plan; past the free cap, a free workspace is slowed down. */
656 gitOperationsIncluded?: number;
657 gitOperationsFreeCap?: number;
658 /** Private storage on the plan before it is charged. */
659 planPrivateStorageBytes?: number;
660 /** A new paid workspace's ceiling in its first month. */
661 paidStartCeilingMicros?: number;
662 /** The most a one-click goodwill credit can cost g1t. */
663 overageForgiveCostMicros?: number;
664};
665
666/**
667 * A workspace's trial credit: one grant per workspace, made the first time
668 * it uses something, out of a pool that resets each calendar month. Mirrors
669 * `Trial` in `crates/contracts/src/billing.rs`.
670 */
671export type Trial = {
672 open: boolean;
673 usedMicros: number;
674 limitMicros: number;
675 /** No longer used: trials do not end on a date. */
676 endsAt: string | null;
677 /** Why it is closed: `off`, `used` (this workspace's grant is spent) or `pool` (this month's are given out). */
678 reason: "off" | "ended" | "used" | "pool" | null;
679 /** Whether the workspace has its grant already. */
680 granted?: boolean;
681 /** With `pool`: when new trials start again, the first of next month. */
682 waitsUntil?: string | null;
683};
684
685/**
686 * What a workspace pays a monthly price for: the g1t plan (`plan`). Deployments
687 * are part of it; `has_feature` for `deployments` answers whether the workspace
688 * has the plan. Mirrors `Feature` in `crates/contracts/src/billing.rs`.
689 */
690export type Feature = "plan" | "deployments";
691
692/** What the g1t plan includes for deployments each month. Mirrors `deployments_allowance`. */
693export const DEPLOYMENTS_ALLOWANCE = {
694 apps: 10,
695 requests: 1_000_000,
696 cpuMs: 3_000_000,
697 /** Build time included: 200 minutes. Billing's `DEPLOYMENTS_BUILD_SECONDS` decides. */
698 buildSeconds: 12_000,
699 /** What Cloudflare charges g1t past that, in millionths of a dollar. */
700 microsPerAppMonth: 20_000,
701 microsPerMillionRequests: 300_000,
702 microsPerMillionCpuMs: 20_000,
703 /** One second of a build's sandbox past the included build time: a fallback; billing charges the price book's `build_second`. */
704 microsPerBuildSecond: 15,
705 /** Custom domains across the workspace, and what each one past that costs g1t a month. */
706 customDomains: 3,
707 microsPerDomainMonth: 100_000,
708} as const;
709
710export type FeaturePlan = {
711 feature: Feature;
712 title: string;
713 /** Charged every month while the plan is on, in cents. */
714 monthlyCents: number;
715 /** What the price includes, one line each. */
716 includes: string[];
717 /** How usage past the allowance is charged. */
718 overage: string;
719};
720
721export type SubscriptionStatus = "active" | "canceling" | "past_due" | "canceled";
722
723export type Subscription = {
724 feature: Feature;
725 status: SubscriptionStatus;
726 /** RFC 3339: when the period paid for ends. */
727 periodEnd: string | null;
728 startedBy: string;
729 startedAt: string;
730};
731
732/** A feature as a workspace sees it. */
733export type FeatureState = {
734 plan: FeaturePlan;
735 subscription: Subscription | null;
736 /** Whether the feature works for the workspace now. */
737 on: boolean;
738 /** On without a plan: comped terms, or given by g1t. Nothing to pay or turn off. */
739 included?: boolean;
740};
741
742export interface BillingApi {
743 status(): Promise<BillingStatus>;
744 /** Members of the workspace only. */
745 account(workspace: string, viewer: Viewer): Promise<Result<BillingAccount>>;
746 /** Newest first. Members of the workspace only. */
747 ledger(workspace: string, viewer: Viewer): Promise<Result<LedgerEntry[]>>;
748 /** A month of the ledger, grouped by `day` (default) or `project`. Members only. */
749 statement(workspace: string, viewer: Viewer, month?: string | null, group?: "day" | "project"): Promise<Result<Statement>>;
750 /** One statement line's entries, 50 at a time; `before` is the last id seen. */
751 statementEntries(
752 workspace: string,
753 viewer: Viewer,
754 filter: { month: string; kind: string; day?: string | null; project?: string | null; before?: string | null },
755 ): Promise<Result<LedgerEntry[]>>;
756 /** What the workspace's agents cost since `since`, broken down. Members only. */
757 usage(workspace: string, viewer: Viewer, since: string): Promise<Result<Usage>>;
758 /**
759 * Prepays usage ($25 at least) and returns the page to send the person to:
760 * by card with 3-D Secure, or by bank transfer from $1,000. Owners only.
761 * The payment's id comes back to `returnUrl` as `session`.
762 */
763 checkout(
764 actor: User,
765 workspace: string,
766 amountCents: number,
767 returnUrl: string,
768 method?: "card" | "bank_transfer",
769 ): Promise<Result<{ url: string }>>;
770 /**
771 * Stripe's hosted billing page for the workspace: card, invoices, billing
772 * email and address. g1t never handles card numbers. Owners only.
773 */
774 billingPortal(actor: User, workspace: string, returnUrl: string): Promise<Result<{ url: string }>>;
775 /** Credits a payment once the processor says it was made. Safe to repeat. */
776 confirm(workspace: string, viewer: Viewer, session: string): Promise<Result<BillingAccount>>;
777 /**
778 * Whether a workspace may start an agent now, asked before anything is
779 * opened for it. A failure, with the reason to show, when it has no credit.
780 */
781 canStart(workspace: string): Promise<Result<boolean>>;
782 /** A workspace's free allowance on g1t's hosted models; `exempt` are open to them anyway. */
783 trial(workspace: string, exempt: string[]): Promise<Trial>;
784 /**
785 * Asks whether a workspace may start an agent and opens the run it will be
786 * charged for. Null when billing is off; a failure when there is no credit.
787 */
788 /** Every paid feature and the workspace's plan for each. Members only. */
789 features(workspace: string, viewer: Viewer): Promise<Result<FeatureState[]>>;
790 /**
791 * Starts the card page for a feature's monthly plan. Owners only. The
792 * page's id comes back to `returnUrl` as `session`.
793 */
794 subscribe(actor: User, workspace: string, feature: Feature, returnUrl: string): Promise<Result<{ url: string }>>;
795 /** Turns the feature on once the plan is paid for. Safe to repeat. */
796 confirmSubscription(workspace: string, viewer: Viewer, session: string): Promise<Result<FeatureState>>;
797 /** Ends a plan at the end of its period, or (`resume`) takes that back. Owners only. */
798 cancelSubscription(actor: User, workspace: string, feature: Feature, resume?: boolean): Promise<Result<FeatureState>>;
799 /** Whether a feature works for a workspace now; a failure with the reason when not. */
800 hasFeature(workspace: string, feature: Feature): Promise<Result<boolean>>;
801 /**
802 * Usage past a plan's allowance, charged from credit at cost plus the
803 * margin, once per `reference`. False if it was charged before.
804 */
805 chargeFeature(charge: {
806 workspace: string;
807 feature: Feature;
808 costMicros: number;
809 description: string;
810 repo?: string | null;
811 reference: string;
812 /** For a build: how long it ran, so the plan's included build time pays for what it can. */
813 buildSeconds?: number | null;
814 }): Promise<Result<boolean>>;
815 /**
816 * What a source cost g1t so far this month, so the workspace's limit
817 * counts it now. Replaces the last report. Billing charges `context`
818 * and `security` itself once the month is over; `deployments` charges
819 * its own.
820 */
821 notePending(workspace: string, source: "deployments" | "context" | "security", costMicros: number): Promise<boolean>;
822 /** What the workspace may do now: its plan, caps, pause, trial, and what the plan gives it. */
823 entitlements(workspace: string): Promise<Entitlements>;
824 /**
825 * Holds a start's estimated cost before the work starts. A failure's code says why not:
826 * `paused`, `limit`, `not_paid`, `trial_used` or `oss_pool_empty`, with a message for the owner.
827 */
828 reserve(reservation: {
829 workspace: string;
830 repo: RepoPath;
831 public: boolean;
832 kind: ComputeKind;
833 /** The most the work is expected to cost g1t, before the margin. */
834 estimateMicros: number;
835 }): Promise<Result<ReservationHeld>>;
836 /** Releases a reservation's hold with what the work cost g1t, before the margin. Safe to repeat. */
837 settle(reservationId: string, actualMicros: number): Promise<Result<boolean>>;
838 /** Stripe's page to save and verify a card (3-D Secure, never charged). Owners only. */
839 cardCheck(actor: User, workspace: string, returnUrl: string): Promise<Result<{ url: string }>>;
840 /** Records the card check once Stripe says it passed, and grants the trial if it can. Safe to repeat. */
841 confirmCardCheck(workspace: string, viewer: Viewer, session: string): Promise<Result<Entitlements>>;
842 /** An owner asks for a higher limit, or for help with usage past what was meant. */
843 requestLimit(
844 actor: User,
845 workspace: string,
846 request: { kind: "limit" | "overage"; amountMicros: number; reason: string; expectedMonthlyMicros: number },
847 ): Promise<Result<LimitRequest>>;
848 /** The workspace's requests and their answers, newest first. Members only. */
849 limitRequests(workspace: string, viewer: Viewer): Promise<Result<LimitRequest[]>>;
850 /**
851 * The owners' own caps on agents: one run's spend ($0.10 to $100) and one issue's ($1 to $1,000).
852 * Null goes back to the default ($2 and $10). A cap staff set wins. Owners only.
853 */
854 setCaps(actor: User, workspace: string, caps: { runCapMicros: number | null; issueCapMicros: number | null }): Promise<Result<Entitlements>>;
855 /** An owner's answer to a spend spike: keep going for 24 hours, or stop. */
856 confirmSpike(actor: User, workspace: string, keepGoing: boolean): Promise<Result<Entitlements>>;
857 /** Every metered price and the recent changes. Public. */
858 prices(): Promise<PriceBook>;
859 /** A workspace's limit, for its members. */
860 limit(workspace: string, viewer: Viewer): Promise<Result<Limit>>;
861 /** The same, for the services that enforce it. */
862 checkLimit(workspace: string): Promise<Result<Limit>>;
863 /**
864 * The owners' own monthly limit, up to what is available; null goes back
865 * to the default, and `useFullLimit` uses everything available. Owners only.
866 */
867 setSpendLimit(
868 actor: User,
869 workspace: string,
870 spendLimitMicros: number | null,
871 useFullLimit?: boolean,
872 /** Use the one-time raise: up to twice the highest ceiling, once per workspace. */
873 raiseOnce?: boolean,
874 ): Promise<Result<Limit>>;
875 /** The workspace's invoices from g1t, newest first. Members only. */
876 invoices(workspace: string, viewer: Viewer): Promise<Result<WorkspaceInvoice[]>>;
877 /**
878 * How long a sandbox ran for a workspace, reported when it stops. Its
879 * cost is recorded and every second is charged, from the first. False if
880 * `reference` was recorded before.
881 */
882 recordSandbox(usage: {
883 workspace: string;
884 seconds: number;
885 description: string;
886 repo?: string | null;
887 reference: string;
888 /** What ran; checks, workflows and the merge queue on public repositories may use the open-source pool. */
889 kind?: ComputeKind | null;
890 /** vCPU-seconds used, when the sandbox can tell: the run is priced on its own CPU. */
891 cpuSeconds?: number | null;
892 /** The reservation it started under, settled with this cost. */
893 reservationId?: string | null;
894 }): Promise<Result<boolean>>;
895 startRun(run: {
896 workspace: string;
897 repo: RepoPath;
898 number: number;
899 task: string;
900 model: string;
901 /** `workspace` when the run uses the workspace's own model provider. */
902 billedTo?: "g1t" | "workspace";
903 /** The model session's id, so the run can be settled at AI Gateway's price. */
904 session?: string | null;
905 }): Promise<Result<RunTicket | null>>;
906}
907
908
909/** One slice of usage: what it was for, what it cost, how many runs. */
910export type UsageSlice = { key: string; micros: number; runs: number };
911
912/** What a workspace's agents cost over a period. */
913export type Usage = {
914 since: string;
915 /** Charged, including g1t's margin. */
916 spentMicros: number;
917 /** What g1t's model provider charged, before the margin. */
918 costMicros: number;
919 /** What runs on the workspace's own provider cost there, estimated. Not charged by g1t. */
920 providerMicros: number;
921 /** What the runs used, at cost: g1t's models and the workspace's own provider together. */
922 usedMicros: number;
923 /** g1t charges nothing for now; the slices then measure usage at cost. */
924 free: boolean;
925 runs: number;
926 /** Spend per day and task, keyed `YYYY-MM-DD/task`. */
927 byDay: UsageSlice[];
928 byTask: UsageSlice[];
929 byRepo: UsageSlice[];
930 /** Keyed `namespace/name#number`. */
931 byPull: UsageSlice[];
932 byModel: UsageSlice[];
933 /** Credit bought in the period. */
934 addedMicros: number;
935};