@fleetless/contracts 6.2.0 → 6.3.0-next.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (31) hide show
  1. package/CHANGELOG.md +40 -0
  2. package/artifacts/openapi.json +4322 -1887
  3. package/artifacts/routes.json +326 -2
  4. package/artifacts/schema/billing-address.schema.json +49 -0
  5. package/artifacts/schema/billing-cancel-request.schema.json +12 -0
  6. package/artifacts/schema/billing-change-request.schema.json +60 -0
  7. package/artifacts/schema/billing-change-response.schema.json +765 -0
  8. package/artifacts/schema/billing-details-update.schema.json +25 -0
  9. package/artifacts/schema/billing-details.schema.json +176 -0
  10. package/artifacts/schema/billing-invoice.schema.json +76 -0
  11. package/artifacts/schema/billing-view.schema.json +671 -0
  12. package/artifacts/schema/checkout-request.schema.json +252 -0
  13. package/artifacts/schema/checkout-response.schema.json +22 -0
  14. package/artifacts/schema/checkout-status.schema.json +49 -0
  15. package/artifacts/schema/payment-method-change-request.schema.json +19 -0
  16. package/artifacts/schema/payment-method.schema.json +80 -0
  17. package/artifacts/schema/payment-provider-unavailable-details.schema.json +29 -0
  18. package/artifacts/schema/vat-id-check-request.schema.json +22 -0
  19. package/artifacts/schema/vat-id-check-response.schema.json +36 -0
  20. package/dist/billing.d.ts +778 -0
  21. package/dist/billing.js +441 -0
  22. package/dist/errors.d.ts +17 -5
  23. package/dist/errors.js +31 -0
  24. package/dist/index.d.ts +4 -2
  25. package/dist/index.js +5 -1
  26. package/dist/plans.d.ts +9 -9
  27. package/dist/realtime.d.ts +2 -2
  28. package/dist/rest.d.ts +3 -3
  29. package/dist/routes.d.ts +1 -1
  30. package/dist/routes.js +181 -4
  31. package/package.json +1 -1
@@ -16,6 +16,10 @@
16
16
  "id": "org",
17
17
  "title": "Org"
18
18
  },
19
+ {
20
+ "id": "billing",
21
+ "title": "Billing"
22
+ },
19
23
  {
20
24
  "id": "users",
21
25
  "title": "Team"
@@ -6414,7 +6418,7 @@
6414
6418
  "target_state_conflict"
6415
6419
  ],
6416
6420
  "transport": "http",
6417
- "notes": "Owner tier, and **downward only**: this route moves the org to a lower plan or cancels it outright to Basic. It never moves the org up — until payment exists, an upgrade or an add-on is not this route's job at all, and is handled today as a Feedback request that Fleetless then applies through the admin route. `409 target_state_conflict` names `target_plan` with rule `not_lower` when the chosen plan is not below the org's current one; with rule `locked_basic_only` when the org is locked (`orgLock`) and the chosen plan is anything but Basic; and with rule `migration_basic_only` when the org is still on the beta, awaiting the switch to priced plans, and the chosen plan is anything but Basic — that choice is exactly what the org lands on at the switch. An owner is never named in `keep` and always stays, but still counts against the target plan's `seats`; `409 plan_limit` names `seats` when the owners alone already exceed it, and names whichever other limit `keep` still exceeds otherwise. `keep` is `null` when the org's current usage already fits the target plan outright and nothing is deleted; named, it lists exactly the robots, apps, app users and developers that stay. The choice is stored as `pending_change` and takes effect at `period_ends_at` — **everything of the chosen kind not named in `keep` is deleted at that instant, never before** — except a choice made while the org is locked, which takes effect at once, and a beta org's choice, which takes effect at the switch date instead. Anything created while the choice is pending is checked against the target plan too and, when it passes, is folded into `keep`, so exactly what the confirmation counted is what is actually deleted. A later `PUT` replaces a still-pending choice outright."
6421
+ "notes": "Owner tier, and **downward only**: this route moves the org to a lower plan or cancels it outright to Basic. It never moves the org up — an upgrade or an add-on goes through the billing routes instead (`POST /api/billing/checkout`, `POST /api/billing/change`). `409 target_state_conflict` names `target_plan` with rule `not_lower` when the chosen plan is not below the org's current one; with rule `locked_basic_only` when the org is locked (`orgLock`) and the chosen plan is anything but Basic; and with rule `migration_basic_only` when the org is still on the beta, awaiting the switch to priced plans, and the chosen plan is anything but Basic — that choice is exactly what the org lands on at the switch. An owner is never named in `keep` and always stays, but still counts against the target plan's `seats`; `409 plan_limit` names `seats` when the owners alone already exceed it, and names whichever other limit `keep` still exceeds otherwise. `keep` is `null` when the org's current usage already fits the target plan outright and nothing is deleted; named, it lists exactly the robots, apps, app users and developers that stay. The choice is stored as `pending_change` and takes effect at `period_ends_at` — **everything of the chosen kind not named in `keep` is deleted at that instant, never before** — except a choice made while the org is locked, which takes effect at once, and a beta org's choice, which takes effect at the switch date instead. Anything created while the choice is pending is checked against the target plan too and, when it passes, is folded into `keep`, so exactly what the confirmation counted is what is actually deleted. A later `PUT` replaces a still-pending choice outright."
6418
6422
  },
6419
6423
  {
6420
6424
  "method": "DELETE",
@@ -6464,10 +6468,330 @@
6464
6468
  "not_found",
6465
6469
  "validation_error",
6466
6470
  "plan_limit",
6471
+ "rate_limited",
6472
+ "target_state_conflict"
6473
+ ],
6474
+ "transport": "http",
6475
+ "notes": "Every public host answers `404 not_found` for every `/api/admin/*` path — this route is reachable only on the cloud's private address — and that same address answers `404` here too while `OPS_API_TOKEN` is not configured, so a door with no key behind it reads exactly like one nobody opened. An unknown `:id` is the same `404`. Applies at once, never queued as a `pending_change`, and only when the org's current usage fits the result: `409 plan_limit` when a lower plan, a lowered override or a removed add-on would leave the org over a limit — the operator never deletes an org's things, only the owner's own choice does. On success it also withdraws any `pending_change`, ends a beta org's wait for the switch and lifts a lock. Audited as `org.plan_changed` with the `fleetless` actor, never a developer's — the row names what an operator did, not who in the org asked for it. `409 target_state_conflict` names `currency` or `period_ends_at` with rule `billed` when the org has a billing account in `active` or `past_due` and the request names either field: both are fixed at the first payment and billing, not the operator, owns them from then on. The plan itself keeps working on a billed org — a plan, an add-on or an override the operator sets here is charged from the next renewal, except `enterprise` and `basic`, which the sweep cancels the billing account for instead."
6476
+ },
6477
+ {
6478
+ "method": "GET",
6479
+ "path": "/api/billing",
6480
+ "section": "billing",
6481
+ "summary": "Reads the org's billing account, payment method and invoices.",
6482
+ "audience": "developer",
6483
+ "auth": "developer",
6484
+ "rateLimited": false,
6485
+ "ownerTier": true,
6486
+ "status": 200,
6487
+ "params": [],
6488
+ "query": null,
6489
+ "request": null,
6490
+ "response": "billing-view",
6491
+ "errors": [
6492
+ "unauthorized",
6493
+ "token_expired",
6494
+ "token_revoked",
6495
+ "tier_required"
6496
+ ],
6497
+ "transport": "http",
6498
+ "notes": "`available: false` when `MOLLIE_API_KEY` is not configured — this cloud takes no payments, and every mutating route on this page answers `503 billing_unavailable` instead of acting. `account` is `null` before the org has ever checked out; the plan and its limits still come from `GET /api/org/plan` (#103) and are not repeated here."
6499
+ },
6500
+ {
6501
+ "method": "POST",
6502
+ "path": "/api/billing/checkout",
6503
+ "section": "billing",
6504
+ "summary": "Starts a Mollie checkout for a plan, or an upgrade paid at once.",
6505
+ "audience": "developer",
6506
+ "auth": "developer",
6507
+ "rateLimited": true,
6508
+ "ownerTier": true,
6509
+ "status": 201,
6510
+ "params": [],
6511
+ "query": null,
6512
+ "request": "checkout-request",
6513
+ "response": "checkout-response",
6514
+ "errors": [
6515
+ "unauthorized",
6516
+ "token_expired",
6517
+ "token_revoked",
6518
+ "tier_required",
6519
+ "validation_error",
6520
+ "plan_limit",
6521
+ "target_state_conflict",
6522
+ "rate_limited",
6523
+ "billing_unavailable",
6524
+ "payment_provider_unavailable"
6525
+ ],
6526
+ "transport": "http",
6527
+ "notes": "Rate limited on the `billing.checkout` bucket, same as `POST /api/billing/payment-method` and `POST /api/billing/invoices/:id/pay` — the three routes that mint a Mollie checkout. `400 validation_error` names who may not pay with these rules: `{ field: 'billing.address.country', rule: 'eu_person' }` for a person in another EU country, `{ field: 'billing.vat_id', rule: 'vat_id_required' }` for a company there with no VAT ID, `{ field: 'billing.vat_id', rule: 'vat_id_invalid' }` once VIES has said so, and `{ field: 'accept_withdrawal', rule: 'required' }` for a person who did not confirm it. `409 target_state_conflict` names `plan` with rule `already_billed` when the org already has an `active` or `past_due` billing account — checkout is for the first payment only, every later change is `POST /api/billing/change`. `checkout_url` is Mollie's hosted page; the return lands on `<console>/settings/billing?checkout=<checkout_id>`, which polls `GET /api/billing/checkout/:id` until the webhook — or the poll itself — has reconciled the payment. `409 plan_limit` is the org's own usage against the plan being bought."
6528
+ },
6529
+ {
6530
+ "method": "GET",
6531
+ "path": "/api/billing/checkout/:id",
6532
+ "section": "billing",
6533
+ "summary": "Reads a checkout's status, for the return page's poll.",
6534
+ "audience": "developer",
6535
+ "auth": "developer",
6536
+ "rateLimited": false,
6537
+ "ownerTier": true,
6538
+ "status": 200,
6539
+ "params": [
6540
+ {
6541
+ "name": "id",
6542
+ "description": "The checkout id from `checkoutResponse.checkout_id`, carried on the return URL."
6543
+ }
6544
+ ],
6545
+ "query": null,
6546
+ "request": null,
6547
+ "response": "checkout-status",
6548
+ "errors": [
6549
+ "unauthorized",
6550
+ "token_expired",
6551
+ "token_revoked",
6552
+ "tier_required",
6553
+ "not_found"
6554
+ ],
6555
+ "transport": "http",
6556
+ "notes": "Calls the same `reconcilePayment(deps, molliePaymentId)` the webhook calls, so a return page that lands before the webhook does still sees the payment applied — this route, not the webhook, is what the dev stack and the test-mode suite rely on, since Mollie refuses an unreachable webhook URL. `plan` is the org's plan after applying, unchanged unless `purpose` is `upgrade` and `status` is `paid`."
6557
+ },
6558
+ {
6559
+ "method": "POST",
6560
+ "path": "/api/billing/vat-id/check",
6561
+ "section": "billing",
6562
+ "summary": "Checks a VAT ID against VIES, for the checkout form and the details page.",
6563
+ "audience": "developer",
6564
+ "auth": "developer",
6565
+ "rateLimited": true,
6566
+ "ownerTier": true,
6567
+ "status": 200,
6568
+ "params": [],
6569
+ "query": null,
6570
+ "request": "vat-id-check-request",
6571
+ "response": "vat-id-check-response",
6572
+ "errors": [
6573
+ "unauthorized",
6574
+ "token_expired",
6575
+ "token_revoked",
6576
+ "tier_required",
6577
+ "validation_error",
6467
6578
  "rate_limited"
6468
6579
  ],
6469
6580
  "transport": "http",
6470
- "notes": "Every public host answers `404 not_found` for every `/api/admin/*` path — this route is reachable only on the cloud's private address — and that same address answers `404` here too while `OPS_API_TOKEN` is not configured, so a door with no key behind it reads exactly like one nobody opened. An unknown `:id` is the same `404`. Applies at once, never queued as a `pending_change`, and only when the org's current usage fits the result: `409 plan_limit` when a lower plan, a lowered override or a removed add-on would leave the org over a limit — the operator never deletes an org's things, only the owner's own choice does. On success it also withdraws any `pending_change`, ends a beta org's wait for the switch and lifts a lock. Audited as `org.plan_changed` with the `fleetless` actor, never a developer's — the row names what an operator did, not who in the org asked for it."
6581
+ "notes": "Rate limited on its own `billing.vat_check` bucket, sized for a form checked on blur rather than for a checkout. Never refuses for an `unverified` VIES answer — the checkout itself accepts `unverified` and the hourly sweep re-checks it — this route only reports what VIES currently says, in a different place for the same ID, entered either at checkout or on `PATCH /api/billing/details`."
6582
+ },
6583
+ {
6584
+ "method": "PATCH",
6585
+ "path": "/api/billing/details",
6586
+ "section": "billing",
6587
+ "summary": "Edits the billing account's invoice email or VAT ID.",
6588
+ "audience": "developer",
6589
+ "auth": "developer",
6590
+ "rateLimited": false,
6591
+ "ownerTier": true,
6592
+ "status": 200,
6593
+ "params": [],
6594
+ "query": null,
6595
+ "request": "billing-details-update",
6596
+ "response": "billing-view",
6597
+ "errors": [
6598
+ "unauthorized",
6599
+ "token_expired",
6600
+ "token_revoked",
6601
+ "tier_required",
6602
+ "validation_error",
6603
+ "not_found"
6604
+ ],
6605
+ "transport": "http",
6606
+ "notes": "`404 not_found` when the org has no billing account yet — there is nothing here to edit before the first checkout. A new `vat_id` is re-checked through VIES the same way `POST /api/billing/vat-id/check` does; a valid ID entered here lifts the block a definitive `invalid` answer placed on the next renewal."
6607
+ },
6608
+ {
6609
+ "method": "POST",
6610
+ "path": "/api/billing/change",
6611
+ "section": "billing",
6612
+ "summary": "Moves the org's plan, cycle or add-ons, charging increases at once.",
6613
+ "audience": "developer",
6614
+ "auth": "developer",
6615
+ "rateLimited": false,
6616
+ "ownerTier": true,
6617
+ "status": 200,
6618
+ "params": [],
6619
+ "query": null,
6620
+ "request": "billing-change-request",
6621
+ "response": "billing-change-response",
6622
+ "errors": [
6623
+ "unauthorized",
6624
+ "token_expired",
6625
+ "token_revoked",
6626
+ "tier_required",
6627
+ "validation_error",
6628
+ "plan_limit",
6629
+ "target_state_conflict",
6630
+ "billing_unavailable",
6631
+ "payment_provider_unavailable"
6632
+ ],
6633
+ "transport": "http",
6634
+ "notes": "The body names the **absolute** target — plan, cycle and add-ons — never a delta: the route compares it with the org's current state and splits the difference. The increasing part is charged now, through the same proration `changeNetCents` computes, and takes effect the moment Mollie accepts the `recurring` payment with anything but `failed`, `canceled` or `expired` — cards answer within seconds, SEPA stays `pending` for days; if Mollie refuses or does not answer, nothing is applied and this answers `502 payment_provider_unavailable` instead. The decreasing part — a lower plan, yearly → monthly, fewer add-ons — is stored as a pending change and applied at the period's end, same as `PUT /api/org/plan/change`; a lower plan over the target's limits answers `409 plan_limit` and the console opens #103's choose-what-stays page. Any increase first withdraws a pending downgrade or cancel, exactly as the admin route does. `409 target_state_conflict` names `billing` with rule `no_account` (no checkout yet — use `POST /api/billing/checkout`), `past_due` (an open invoice has to be paid first) or `no_valid_mandate` (the payment method needs renewing first, `POST /api/billing/payment-method`). `charged` in the response is the invoice from the part charged now, or `null` when the whole request was a decrease."
6635
+ },
6636
+ {
6637
+ "method": "POST",
6638
+ "path": "/api/billing/payment-method",
6639
+ "section": "billing",
6640
+ "summary": "Starts a Mollie checkout for a new payment method.",
6641
+ "audience": "developer",
6642
+ "auth": "developer",
6643
+ "rateLimited": true,
6644
+ "ownerTier": true,
6645
+ "status": 201,
6646
+ "params": [],
6647
+ "query": null,
6648
+ "request": "payment-method-change-request",
6649
+ "response": "checkout-response",
6650
+ "errors": [
6651
+ "unauthorized",
6652
+ "token_expired",
6653
+ "token_revoked",
6654
+ "tier_required",
6655
+ "validation_error",
6656
+ "not_found",
6657
+ "rate_limited",
6658
+ "billing_unavailable",
6659
+ "payment_provider_unavailable"
6660
+ ],
6661
+ "transport": "http",
6662
+ "notes": "Rate limited on the `billing.checkout` bucket, same as `POST /api/billing/checkout`. Creates a `first` payment on the existing Mollie customer: with an open invoice, the new payment **is** that invoice's amount, for any method the currency allows, and pays it; without one, `card` and `paypal` use a zero amount and `sepa` is refused — Mollie allows a zero-amount first payment for card and PayPal only — `400 validation_error` with `{ field: 'method', rule: 'sepa_needs_open_invoice' }`. SEPA is also refused outside the org's EUR currency, `{ field: 'method', rule: 'sepa_eur_only' }`. `404 not_found` when the org has no billing account yet. When the new mandate turns `valid`, every other mandate of the customer is revoked."
6663
+ },
6664
+ {
6665
+ "method": "POST",
6666
+ "path": "/api/billing/cancel",
6667
+ "section": "billing",
6668
+ "summary": "Schedules the org's plan to cancel to Basic at the period's end.",
6669
+ "audience": "developer",
6670
+ "auth": "developer",
6671
+ "rateLimited": false,
6672
+ "ownerTier": true,
6673
+ "status": 200,
6674
+ "params": [],
6675
+ "query": null,
6676
+ "request": "billing-cancel-request",
6677
+ "requestOptional": true,
6678
+ "response": "billing-view",
6679
+ "errors": [
6680
+ "unauthorized",
6681
+ "token_expired",
6682
+ "token_revoked",
6683
+ "tier_required",
6684
+ "validation_error",
6685
+ "not_found",
6686
+ "plan_limit"
6687
+ ],
6688
+ "transport": "http",
6689
+ "notes": "A body is optional — `reason` alone, and nobody but Fleetless reads it. `404 not_found` when the org has no billing account to cancel. Takes effect at the period's end, nothing credited or refunded; over Basic's limits this is refused `409 plan_limit` and the console sends the owner to #103's choose-what-stays page instead, the same as a plan downgrade. \"Cancel at period end\" is not a flag here — it reads as `pending_change.target_plan === 'basic'` on `GET /api/org/plan`."
6690
+ },
6691
+ {
6692
+ "method": "POST",
6693
+ "path": "/api/billing/resume",
6694
+ "section": "billing",
6695
+ "summary": "Withdraws a scheduled cancel, keeping the current plan.",
6696
+ "audience": "developer",
6697
+ "auth": "developer",
6698
+ "rateLimited": false,
6699
+ "ownerTier": true,
6700
+ "status": 200,
6701
+ "params": [],
6702
+ "query": null,
6703
+ "request": null,
6704
+ "response": "billing-view",
6705
+ "errors": [
6706
+ "unauthorized",
6707
+ "token_expired",
6708
+ "token_revoked",
6709
+ "tier_required",
6710
+ "not_found"
6711
+ ],
6712
+ "transport": "http",
6713
+ "notes": "`404 not_found` when nothing is pending — there is no scheduled cancel to withdraw. The org stays on its current plan, unchanged."
6714
+ },
6715
+ {
6716
+ "method": "GET",
6717
+ "path": "/api/billing/invoices/:id/pdf",
6718
+ "section": "billing",
6719
+ "summary": "Downloads one invoice's rendered PDF.",
6720
+ "audience": "developer",
6721
+ "auth": "developer",
6722
+ "rateLimited": false,
6723
+ "ownerTier": true,
6724
+ "status": 200,
6725
+ "params": [
6726
+ {
6727
+ "name": "id",
6728
+ "description": "The invoice id, from `billingInvoice.id` in `GET /api/billing`'s `invoices`."
6729
+ }
6730
+ ],
6731
+ "query": null,
6732
+ "request": null,
6733
+ "response": null,
6734
+ "contentType": "application/pdf",
6735
+ "errors": [
6736
+ "unauthorized",
6737
+ "token_expired",
6738
+ "token_revoked",
6739
+ "tier_required",
6740
+ "not_found"
6741
+ ],
6742
+ "transport": "http",
6743
+ "notes": "Rendered once, with `pdfkit`, when the invoice is issued, and stored as bytes — an issued invoice never changes, so this always answers the same PDF for the same id. `404 not_found` for an unknown id or one from another org."
6744
+ },
6745
+ {
6746
+ "method": "POST",
6747
+ "path": "/api/billing/invoices/:id/pay",
6748
+ "section": "billing",
6749
+ "summary": "Pays one open invoice, through a new Mollie payment.",
6750
+ "audience": "developer",
6751
+ "auth": "developer",
6752
+ "rateLimited": true,
6753
+ "ownerTier": true,
6754
+ "status": 201,
6755
+ "params": [
6756
+ {
6757
+ "name": "id",
6758
+ "description": "The invoice id, from `billingInvoice.id`, of the open invoice to pay."
6759
+ }
6760
+ ],
6761
+ "query": null,
6762
+ "request": null,
6763
+ "response": "checkout-response",
6764
+ "errors": [
6765
+ "unauthorized",
6766
+ "token_expired",
6767
+ "token_revoked",
6768
+ "tier_required",
6769
+ "not_found",
6770
+ "target_state_conflict",
6771
+ "rate_limited",
6772
+ "billing_unavailable",
6773
+ "payment_provider_unavailable"
6774
+ ],
6775
+ "transport": "http",
6776
+ "notes": "Rate limited on the `billing.checkout` bucket, same as `POST /api/billing/checkout`. `409 target_state_conflict` names `invoice` with rule `not_open` when the invoice is already `paid` or `uncollectible` — there is nothing left to pay. Paying the open invoice of a locked org unlocks it and keeps the plan running to the period's end, the same as a renewal that succeeds on a retry. `404 not_found` for an unknown id or one from another org."
6777
+ },
6778
+ {
6779
+ "method": "POST",
6780
+ "path": "/api/billing/mollie/webhook",
6781
+ "section": "billing",
6782
+ "summary": "Takes Mollie's payment-changed notification and reconciles the payment.",
6783
+ "audience": "internal",
6784
+ "auth": "none",
6785
+ "rateLimited": true,
6786
+ "ownerTier": false,
6787
+ "status": 200,
6788
+ "params": [],
6789
+ "query": null,
6790
+ "request": null,
6791
+ "response": null,
6792
+ "errors": [],
6793
+ "transport": "http",
6794
+ "notes": "**The body is never trusted**: it names only a payment id (`id=tr_…`, form-encoded, Mollie's own shape), and this route does nothing with it but call `reconcilePayment(deps, molliePaymentId)` — the same function `GET /api/billing/checkout/:id` and the hourly sweep call — which fetches the payment from Mollie itself and applies what Mollie says, idempotently. Rate limited on the `billing.webhook` bucket, per ip, 600/min — generous, because this is Mollie's own infrastructure calling, not a browser. **Every answer is `200`**, including an id this cloud does not recognise, which is logged and otherwise ignored, **except a processing fault, which is `500`** so Mollie retries the notification rather than this cloud losing it. `auth: 'none'` because Mollie signs nothing Fleetless checks here — the payment is only ever trusted once fetched back from Mollie's own API with the configured key."
6471
6795
  },
6472
6796
  {
6473
6797
  "method": "POST",
@@ -0,0 +1,49 @@
1
+ {
2
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
3
+ "type": "object",
4
+ "properties": {
5
+ "line1": {
6
+ "type": "string",
7
+ "minLength": 1,
8
+ "maxLength": 200,
9
+ "description": "Street and number, or the first address line."
10
+ },
11
+ "line2": {
12
+ "anyOf": [
13
+ {
14
+ "type": "string",
15
+ "maxLength": 200
16
+ },
17
+ {
18
+ "type": "null"
19
+ }
20
+ ],
21
+ "description": "A second address line, or `null` when there is none."
22
+ },
23
+ "postal_code": {
24
+ "type": "string",
25
+ "minLength": 1,
26
+ "maxLength": 20,
27
+ "description": "Postal or ZIP code."
28
+ },
29
+ "city": {
30
+ "type": "string",
31
+ "minLength": 1,
32
+ "maxLength": 100,
33
+ "description": "City or town."
34
+ },
35
+ "country": {
36
+ "type": "string",
37
+ "pattern": "^[A-Z]{2}$",
38
+ "description": "The billing country. Decides VAT (`vatFor`) and currency (`currencyForCountry`)."
39
+ }
40
+ },
41
+ "required": [
42
+ "line1",
43
+ "line2",
44
+ "postal_code",
45
+ "city",
46
+ "country"
47
+ ],
48
+ "additionalProperties": false
49
+ }
@@ -0,0 +1,12 @@
1
+ {
2
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
3
+ "type": "object",
4
+ "properties": {
5
+ "reason": {
6
+ "description": "An optional free-text reason. Shown to nobody but Fleetless.",
7
+ "type": "string",
8
+ "maxLength": 500
9
+ }
10
+ },
11
+ "additionalProperties": false
12
+ }
@@ -0,0 +1,60 @@
1
+ {
2
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
3
+ "type": "object",
4
+ "properties": {
5
+ "plan": {
6
+ "description": "The target plan. Omitted leaves the plan as it is.",
7
+ "type": "string",
8
+ "enum": [
9
+ "plus",
10
+ "pro"
11
+ ]
12
+ },
13
+ "cycle": {
14
+ "description": "The target cycle. Omitted leaves the cycle as it is.",
15
+ "type": "string",
16
+ "enum": [
17
+ "monthly",
18
+ "yearly"
19
+ ]
20
+ },
21
+ "addons": {
22
+ "description": "Absolute add-on counts to end up with, not a delta. Omitted leaves add-ons as they are.",
23
+ "type": "object",
24
+ "properties": {
25
+ "seats": {
26
+ "type": "integer",
27
+ "minimum": 0,
28
+ "maximum": 9007199254740991,
29
+ "description": "Extra developer seats, one each."
30
+ },
31
+ "robots": {
32
+ "type": "integer",
33
+ "minimum": 0,
34
+ "maximum": 9007199254740991,
35
+ "description": "Extra robots, one each."
36
+ },
37
+ "apps": {
38
+ "type": "integer",
39
+ "minimum": 0,
40
+ "maximum": 9007199254740991,
41
+ "description": "Extra apps, one each."
42
+ },
43
+ "app_user_packs": {
44
+ "type": "integer",
45
+ "minimum": 0,
46
+ "maximum": 9007199254740991,
47
+ "description": "Packs of five extra app users."
48
+ },
49
+ "live_video_packs": {
50
+ "type": "integer",
51
+ "minimum": 0,
52
+ "maximum": 9007199254740991,
53
+ "description": "Packs of 250 extra hours of app-user live video per month."
54
+ }
55
+ },
56
+ "additionalProperties": false
57
+ }
58
+ },
59
+ "additionalProperties": false
60
+ }