openapi: 3.1.0 info: title: Fluence Billing API description: "# Introduction\n\nThe Fluence API gives programmatic access to the decentralized Fluence compute marketplace, letting you find, rent, and\nmanage compute resources without the web UI. Integrate Fluence directly into your applications and automated workflows.\n\nBase URL: `https://api.fluence.dev`\n\nThe documentation starts with a high-level overview of design and technology, followed by endpoint reference details.\nFor additional user guides, see the [Fluence Documentation](https://fluence.dev/docs/build/overview).\n\n## Requests\n\nSend HTTPS requests to the Fluence API. All request and response bodies are JSON.\n\n- Set `Content-Type: application/json` for POST, PUT, and PATCH calls.\n- All examples assume the default `Accept: application/json` header.\n\n### Example\n\n```bash\ncurl -X POST https://api.fluence.dev/v1/public_ips \\\n -H \"Content-Type: application/json\" \\\n -H \"Authorization: X-API-KEY \" \\\n -d '{\n \"addressType\": \"V4\",\n \"clusterId\": \"3fa85f64-5717-4562-b3fc-2c963f66afa6\",\n \"name\": \"my-public-ip\"\n }'\n```\n\n### HTTP request methods\n\nThe interface responds to different methods depending on the action required.\n\n| Method | Description |\n|--------|----------------------------------------------------------------------------------------------------|\n| GET | Retrieves resource information. These requests are read-only and won't modify any resources. |\n| POST | Usually used to create new resources. Include all required attributes in the request body as JSON. |\n| PATCH | Update specific attributes of a resource. |\n| PUT | Updates an entire resource by replacing it with the provided data, regardless of current values. |\n| DELETE | Removes resources. |\n\n## Responses\n\nSuccessful requests return `2xx` status codes and a JSON object. \n\nIf an error occurs, Fluence returns an `4xx` or `5xx` status code and a JSON object describing the problem.\n\n### Response codes\n\n| Code | Description |\n|------|--------------------------------------------------------------------------------|\n| 2xx | **Success** - The request was successfully received, understood, and processed |\n| 4xx | **Client Error** - The request contains invalid syntax or cannot be fulfilled |\n| 400 | Bad request - Invalid parameters or malformed JSON |\n| 401 | Unauthorized - API key missing or invalid |\n| 403 | Forbidden - Authenticated but not allowed |\n| 404 | Not found - Resource does not exist |\n| 422 | Unprocessable Entity - Well-formed request but cannot be processed |\n| 500 | Internal server error - Unexpected error on our side. |\n\n### Error Response Format\n\nWhen an error occurs, the response includes a status message and a body in JSON format with an `error` field with\nadditional information. For example:\n\n```bash\n HTTP/1.1 403 Forbidden\n {\n \"error\": \"Auth failed. No such API Key\",\n }\n```\n\n## Authentication\n\nAll endpoints require a jwt token supplied as a Bearer token in the `Authorization` header or an API key provided as the value of the `X-API-KEY` header. For example:\n\n```bash\ncurl -H 'Authorization: Bearer my_authentication_token'\n```\n\n```bash\ncurl -H 'Authorization: X-API-KEY my_api_key'\n```\n\n### Managing API Keys\n\nCreate and manage keys in\nthe [Fluence Console › Settings › API Keys](https://console.fluence.network/settings/api-keys).\n\n- **Read-only keys** – retrieve information only\n- **Full-access keys** – manage all resources\n\n> **Important**: API keys are only displayed once during creation for security reasons. Store them safely - if a key is\n> lost, expired or compromised, you'll need to delete it and create a new one.\n\n### Key Security\n\nAPI keys function as complete authentication credentials, similar to a username/password combination. Therefore:\n\n- Keep your keys secure and never share them\n- Use HTTPS for all API requests\n- Rotate keys periodically\n- Set appropriate expiration dates when creating keys\n- Delete compromised keys immediately\n\n## Cloud-init\n\nOptional `cloudInit.userData` on `POST /v2/vms` lets you bootstrap the VM with a\n[cloud-init](https://cloudinit.readthedocs.io/) configuration. The payload is\nforwarded to the VM backend verbatim; vodopad does not parse or validate the\ncontent beyond a 16 KiB byte cap.\n\n```bash\ncurl -X POST https://api.fluence.dev/v2/vms \\\n -H \"Authorization: X-API-KEY \" \\\n -H \"Content-Type: application/json\" \\\n -d '{\n \"clusterId\": \"...\",\n \"name\": \"demo\",\n \"configurationId\": \"...\",\n \"bootDisk\": { \"...\": \"...\" },\n \"sshKeys\": [\"...\"],\n \"cloudInit\": {\n \"userData\": \"#cloud-config\\nruncmd:\\n - echo hello\\n\"\n }\n }'\n```\n\nNotes:\n\n- **Create-only.** `userData` is meaningful only on `POST /v2/vms`. `PATCH` requests have no `userData` field; sending one is dropped during JSON deserialization.\n- **Ephemeral.** The content is stored only until the VM transitions to `Launched` (or `Failed` on create-deadline, or `Terminating`/`Terminated` on termination), then erased in the same transaction as the status change. After that, listing or fetching the VM returns a non-null `cloudInit` object (no fields) as a permanent marker but never the original content.\n- **Length limit.** Maximum 16 KiB enforced as a **byte count** (UTF-8 multi-byte characters are counted by their byte size, not code-point count). Oversize requests are rejected with `400 Bad Request`.\n- **Privacy.** The content is never echoed back through any API endpoint. With request-body debug logging enabled (`RUST_LOG=vodopad::api::server=debug` or `sqlx=debug`), `userData` will appear in logs — see Operator notes below.\n- **Backend rejection.** Malformed userData (e.g. invalid YAML) is detected by the VM backend during provisioning and surfaces as a `Failed` VM status, not a synchronous `400`.\n\n### Operator notes\n\n- The side-table `crd.user_virtual_machine_cloud_init` keeps a row per VM that ever had cloud-init. After successful handoff to the VM backend (status `Launching` or `Launched`) — or upon terminal cleanup (`Failed` from create-deadline timeout, or `Terminating`/`Terminated` from termination) — the row's `user_data` column is set to `NULL` in the same transaction as the status change. The row itself stays as a permanent \"had cloud-init\" marker — its existence is the source of truth for the non-null `cloudInit` object on the response.\n- WAL/audit logs and `pg_dump` backups can preserve the **pre-wipe** state (raw `user_data` bytes) until they roll over. Treat these artefacts as carrying potentially-sensitive material; restrict access and retention accordingly.\n- Do not enable `RUST_LOG=vodopad::api::server=debug` in production — the request/response body logger (`buffer_and_print` in `api/server.rs`) writes every UTF-8 body verbatim to `tracing::debug!` with no path-based redaction, and runs *before* validation, so even rejected oversize `userData` payloads end up in the log.\n- Do not set `RUST_LOG=sqlx=debug` in production — sqlx debug logs can include bind values, which would expose `userData` for in-flight queries.\n" contact: name: Cloudless Labs license: name: AGPL-3 identifier: AGPL-3 version: 0.10.0 security: - api_key: [] tags: - name: Billing description: Billing and payment related endpoints paths: /v1/billing/charges/aggregated: get: tags: - Billing description: Aggregated charges over a date range. `startDate` is inclusive, `endDate` is inclusive (charges through the end of the `endDate` day are included). Optional `groupBy`/`period` control the aggregation dimension and time bucket; both default to the fixed per-resource grouping. operationId: get_aggregated_charges parameters: - name: resourceId in: query required: false schema: $ref: '#/components/schemas/ResourceId' - name: resourceType in: query required: false schema: $ref: '#/components/schemas/ResourceTypeDto' - name: startDate in: query description: Start of the range, inclusive (charges from the start of this day). required: true schema: type: string format: date - name: endDate in: query description: 'End of the range, inclusive: charges through the end of the endDate day are included.' required: true schema: type: string format: date - name: format in: query required: false schema: $ref: '#/components/schemas/ResponseOutputFormat' - name: groupBy in: query description: Resource dimension to aggregate by. Defaults to the per-resource tuple. required: false schema: $ref: '#/components/schemas/ChargeGroupByDto' - name: period in: query description: Time bucket to split aggregates into. Defaults to no time split. required: false schema: $ref: '#/components/schemas/ChargePeriodDto' responses: '200': description: Aggregated charges content: application/json: schema: type: array items: $ref: '#/components/schemas/TotalChargesEntry' text/csv: schema: type: string '400': description: Bad Request content: application/json: schema: $ref: '#/components/schemas/ErrorBody' example: code: bad_request error: Wrong parameters '401': description: Unauthorized content: application/json: schema: $ref: '#/components/schemas/ErrorBody' example: code: unauthorized error: Auth token invalid or missing '403': description: Forbidden content: application/json: schema: $ref: '#/components/schemas/ErrorBody' example: code: forbidden error: Not allowed to call this endpoint '422': description: Unprocessable Entity content: application/json: schema: $ref: '#/components/schemas/ErrorBody' example: code: unprocessable_entity error: Unable to process the entity '500': description: Internal Server Error content: application/json: schema: $ref: '#/components/schemas/ErrorBody' example: code: internal_server_error error: Unexpected server error security: - access_jwt_token: [] - api_key: - billing:get_spending_history /v1/billing/charges/total: get: tags: - Billing operationId: get_total_charges parameters: - name: resourceId in: query required: false schema: $ref: '#/components/schemas/ResourceId' - name: resourceType in: query required: false schema: $ref: '#/components/schemas/ResourceTypeDto' - name: startDate in: query required: false schema: type: string format: date - name: endDate in: query required: false schema: type: string format: date responses: '200': description: '' content: application/json: schema: $ref: '#/components/schemas/TotalChargesResponse' '400': description: Bad Request content: application/json: schema: $ref: '#/components/schemas/ErrorBody' example: code: bad_request error: Wrong parameters '401': description: Unauthorized content: application/json: schema: $ref: '#/components/schemas/ErrorBody' example: code: unauthorized error: Auth token invalid or missing '403': description: Forbidden content: application/json: schema: $ref: '#/components/schemas/ErrorBody' example: code: forbidden error: Not allowed to call this endpoint '422': description: Unprocessable Entity content: application/json: schema: $ref: '#/components/schemas/ErrorBody' example: code: unprocessable_entity error: Unable to process the entity '500': description: Internal Server Error content: application/json: schema: $ref: '#/components/schemas/ErrorBody' example: code: internal_server_error error: Unexpected server error security: - access_jwt_token: [] - api_key: - billing:get_spending_history /v1/billing/top_up: post: tags: - Billing operationId: Create top-up request requestBody: description: Create a new top-up request content: application/json: schema: $ref: '#/components/schemas/TopUpRequest' required: true responses: '200': description: Top-up request processed successfully content: application/json: schema: $ref: '#/components/schemas/TopUpDto' '400': description: Bad Request content: application/json: schema: $ref: '#/components/schemas/ErrorBody' example: code: bad_request error: Wrong parameters '401': description: Unauthorized content: application/json: schema: $ref: '#/components/schemas/ErrorBody' example: code: unauthorized error: Auth token invalid or missing '403': description: Forbidden content: application/json: schema: $ref: '#/components/schemas/ErrorBody' example: code: forbidden error: Not allowed to call this endpoint '422': description: Unprocessable Entity content: application/json: schema: $ref: '#/components/schemas/ErrorBody' example: code: unprocessable_entity error: Unable to process the entity '500': description: Internal Server Error content: application/json: schema: $ref: '#/components/schemas/ErrorBody' example: code: internal_server_error error: Unexpected server error security: - access_jwt_token: [] - api_key: - billing:top_up /v1/billing/top_up/pending: get: tags: - Billing operationId: Get pending top-up responses: '200': description: Get pending top-up request content: application/json: schema: $ref: '#/components/schemas/PendingTopUpResponse' '400': description: Bad Request content: application/json: schema: $ref: '#/components/schemas/ErrorBody' example: code: bad_request error: Wrong parameters '401': description: Unauthorized content: application/json: schema: $ref: '#/components/schemas/ErrorBody' example: code: unauthorized error: Auth token invalid or missing '403': description: Forbidden content: application/json: schema: $ref: '#/components/schemas/ErrorBody' example: code: forbidden error: Not allowed to call this endpoint '422': description: Unprocessable Entity content: application/json: schema: $ref: '#/components/schemas/ErrorBody' example: code: unprocessable_entity error: Unable to process the entity '500': description: Internal Server Error content: application/json: schema: $ref: '#/components/schemas/ErrorBody' example: code: internal_server_error error: Unexpected server error security: - access_jwt_token: [] - api_key: - billing:get_history /v2/billing/get_history: get: tags: - Billing operationId: get_top_up_history_v2 parameters: - name: start in: query required: false schema: type: string format: date-time - name: end in: query required: false schema: type: string format: date-time - name: status in: query required: false schema: $ref: '#/components/schemas/TopUpStatusDTO' - name: minAmount in: query required: false schema: type: integer format: int64 - name: maxAmount in: query required: false schema: type: integer format: int64 - name: sort in: query required: false schema: $ref: '#/components/schemas/TopUpSortFieldDto' - name: order in: query required: false schema: $ref: '#/components/schemas/SortOrderDto' - name: page in: query required: false schema: type: integer format: u-int64 minimum: 0 - name: perPage in: query required: false schema: type: integer format: u-int64 minimum: 0 responses: '200': description: Get billing history content: application/json: schema: $ref: '#/components/schemas/PaginatedResponse_TopUpDto' '400': description: Bad Request content: application/json: schema: $ref: '#/components/schemas/ErrorBody' example: code: bad_request error: Wrong parameters '401': description: Unauthorized content: application/json: schema: $ref: '#/components/schemas/ErrorBody' example: code: unauthorized error: Auth token invalid or missing '403': description: Forbidden content: application/json: schema: $ref: '#/components/schemas/ErrorBody' example: code: forbidden error: Not allowed to call this endpoint '422': description: Unprocessable Entity content: application/json: schema: $ref: '#/components/schemas/ErrorBody' example: code: unprocessable_entity error: Unable to process the entity '500': description: Internal Server Error content: application/json: schema: $ref: '#/components/schemas/ErrorBody' example: code: internal_server_error error: Unexpected server error security: - access_jwt_token: [] - api_key: - billing:get_history /v2/payment-methods: get: tags: - Billing operationId: discover_payment_methods_v2 responses: '200': description: Enabled payment methods the user can top up with content: application/json: schema: type: array items: $ref: '#/components/schemas/PaymentMethodDto' '400': description: Bad Request content: application/json: schema: $ref: '#/components/schemas/ErrorBody' example: code: bad_request error: Wrong parameters '401': description: Unauthorized content: application/json: schema: $ref: '#/components/schemas/ErrorBody' example: code: unauthorized error: Auth token invalid or missing '403': description: Forbidden content: application/json: schema: $ref: '#/components/schemas/ErrorBody' example: code: forbidden error: Not allowed to call this endpoint '422': description: Unprocessable Entity content: application/json: schema: $ref: '#/components/schemas/ErrorBody' example: code: unprocessable_entity error: Unable to process the entity '500': description: Internal Server Error content: application/json: schema: $ref: '#/components/schemas/ErrorBody' example: code: internal_server_error error: Unexpected server error security: - access_jwt_token: [] - api_key: - billing:top_up /v2/top-ups: get: tags: - Billing operationId: list_top_ups_v2 parameters: - name: start in: query required: false schema: type: string example: '2025-01-01T00:00:00.000Z' - name: end in: query required: false schema: type: string example: '2025-01-01T00:00:00.000Z' - name: sort in: query required: false schema: $ref: '#/components/schemas/TopUpSortFieldDto' - name: order in: query required: false schema: $ref: '#/components/schemas/SortOrderDto' - name: page in: query required: false schema: type: integer format: u-int64 minimum: 0 - name: perPage in: query required: false schema: type: integer format: u-int64 minimum: 0 responses: '200': description: 'The caller''s top-up history (v2-native: cents, typed status)' content: application/json: schema: $ref: '#/components/schemas/PaginatedResponse_TopUpV2Dto' '400': description: Bad Request content: application/json: schema: $ref: '#/components/schemas/ErrorBody' example: code: bad_request error: Wrong parameters '401': description: Unauthorized content: application/json: schema: $ref: '#/components/schemas/ErrorBody' example: code: unauthorized error: Auth token invalid or missing '403': description: Forbidden content: application/json: schema: $ref: '#/components/schemas/ErrorBody' example: code: forbidden error: Not allowed to call this endpoint '422': description: Unprocessable Entity content: application/json: schema: $ref: '#/components/schemas/ErrorBody' example: code: unprocessable_entity error: Unable to process the entity '500': description: Internal Server Error content: application/json: schema: $ref: '#/components/schemas/ErrorBody' example: code: internal_server_error error: Unexpected server error security: - access_jwt_token: [] - api_key: - billing:get_history post: tags: - Billing operationId: create_top_up_v2 requestBody: description: Create a new top-up intent against a payment provider content: application/json: schema: $ref: '#/components/schemas/CreateTopUpV2Request' required: true responses: '201': description: Top-up intent created content: application/json: schema: $ref: '#/components/schemas/CreateTopUpV2Response' '400': description: Bad Request content: application/json: schema: $ref: '#/components/schemas/ErrorBody' example: code: bad_request error: Wrong parameters '401': description: Unauthorized content: application/json: schema: $ref: '#/components/schemas/ErrorBody' example: code: unauthorized error: Auth token invalid or missing '403': description: Forbidden content: application/json: schema: $ref: '#/components/schemas/ErrorBody' example: code: forbidden error: Not allowed to call this endpoint '409': description: An open top-up already exists for the user content: application/json: schema: $ref: '#/components/schemas/ErrorBody' '422': description: Unprocessable Entity content: application/json: schema: $ref: '#/components/schemas/ErrorBody' example: code: unprocessable_entity error: Unable to process the entity '500': description: Internal Server Error content: application/json: schema: $ref: '#/components/schemas/ErrorBody' example: code: internal_server_error error: Unexpected server error '502': description: Payment provider transport error content: application/json: schema: $ref: '#/components/schemas/ErrorBody' security: - access_jwt_token: [] - api_key: - billing:top_up /v2/top-ups/pending: get: tags: - Billing operationId: get_pending_top_up_v2 responses: '200': description: The caller's open (pending) top-up, if any content: application/json: schema: $ref: '#/components/schemas/PendingTopUpV2Response' '400': description: Bad Request content: application/json: schema: $ref: '#/components/schemas/ErrorBody' example: code: bad_request error: Wrong parameters '401': description: Unauthorized content: application/json: schema: $ref: '#/components/schemas/ErrorBody' example: code: unauthorized error: Auth token invalid or missing '403': description: Forbidden content: application/json: schema: $ref: '#/components/schemas/ErrorBody' example: code: forbidden error: Not allowed to call this endpoint '422': description: Unprocessable Entity content: application/json: schema: $ref: '#/components/schemas/ErrorBody' example: code: unprocessable_entity error: Unable to process the entity '500': description: Internal Server Error content: application/json: schema: $ref: '#/components/schemas/ErrorBody' example: code: internal_server_error error: Unexpected server error security: - access_jwt_token: [] - api_key: - billing:get_history /v2/top-ups/{intent_id}: delete: tags: - Billing operationId: cancel_top_up_v2 parameters: - name: intent_id in: path description: Top-up intent to cancel required: true schema: $ref: '#/components/schemas/IntentId' responses: '204': description: Top-up intent canceled '400': description: Bad Request content: application/json: schema: $ref: '#/components/schemas/ErrorBody' example: code: bad_request error: Wrong parameters '401': description: Unauthorized content: application/json: schema: $ref: '#/components/schemas/ErrorBody' example: code: unauthorized error: Auth token invalid or missing '403': description: Forbidden content: application/json: schema: $ref: '#/components/schemas/ErrorBody' example: code: forbidden error: Not allowed to call this endpoint '404': description: Intent not found or not owned by the caller content: application/json: schema: $ref: '#/components/schemas/ErrorBody' '409': description: Intent is already terminal, or is under review awaiting admin resolution content: application/json: schema: $ref: '#/components/schemas/ErrorBody' '422': description: Unprocessable Entity content: application/json: schema: $ref: '#/components/schemas/ErrorBody' example: code: unprocessable_entity error: Unable to process the entity '500': description: Internal Server Error content: application/json: schema: $ref: '#/components/schemas/ErrorBody' example: code: internal_server_error error: Unexpected server error security: - access_jwt_token: [] - api_key: - billing:top_up components: schemas: ResourceTypeDto: type: string enum: - VM - PUBLIC_IP - SECURITY_GROUP - STORAGE - SUBNET - VPC - WORKSPACE TopUpDto: type: object required: - id - userId - currency - amount - copperxFee - copperxGasFee - status - createdAt - updatedAt properties: amount: type: integer format: u-int64 minimum: 0 balanceContractTxAddress: type: - string - 'null' copperxFee: type: integer format: u-int64 minimum: 0 copperxGasFee: type: integer format: u-int64 minimum: 0 copperxSessionId: type: - string - 'null' copperxSessionUrl: type: - string - 'null' createdAt: type: string example: '2024-01-01T00:00:00Z' currency: $ref: '#/components/schemas/Currency' expiresAt: type: - string - 'null' description: When this top-up's checkout session times out. Omitted for legacy v1 rows. example: '2024-01-01T00:00:00Z' id: $ref: '#/components/schemas/TopUpId' provider: type: - string - 'null' description: 'Payment provider behind this top-up (`copperx`, `stripe`, ...). The `copperxSession*` fields keep their legacy names for back-compat but carry the session of whatever provider is named here.' status: $ref: '#/components/schemas/TopUpStatusDTO' updatedAt: type: string example: '2024-01-01T00:00:00Z' userId: $ref: '#/components/schemas/UserId' VmConfigurationSlug: type: string enum: - cpu-regular-2vcpu-4gb - cpu-regular-4vcpu-8gb - cpu-regular-8vcpu-16gb - cpu-regular-8vcpu-32gb - cpu-regular-16vcpu-32gb - cpu-regular-24vcpu-48gb - cpu-regular-32vcpu-64gb - cpu-regular-64vcpu-128gb - cpu-premium-2vcpu-4gb - cpu-premium-4vcpu-8gb - cpu-premium-8vcpu-16gb - cpu-premium-8vcpu-32gb - cpu-premium-16vcpu-32gb - cpu-premium-24vcpu-48gb - cpu-premium-32vcpu-64gb - cpu-premium-64vcpu-128gb - cpu-shared-2vcpu-2gb - cpu-shared-3vcpu-4gb - cpu-shared-4vcpu-8gb - cpu-shared-8vcpu-16gb - cpu-shared-16vcpu-32gb ErrorCode: type: string description: 'Stable, machine-readable error code carried by every non-2xx response. Clients should branch on `code` rather than parsing the human-readable `error` string. Status-derived variants cover any error that does not supply a more specific code; semantic variants are set explicitly at the construction site (see `VodopadError::code`).' enum: - bad_request - unauthorized - forbidden - not_found - conflict - unprocessable_entity - not_acceptable - insufficient_balance - service_unavailable - internal_server_error Currency: type: string enum: - usdc - usdt - usd CreateTopUpV2Response: type: object required: - intentId - sessionId - provider - providerPaymentId - presentation properties: intentId: $ref: '#/components/schemas/IntentId' presentation: $ref: '#/components/schemas/TopUpPresentation' provider: type: string providerPaymentId: type: string sessionId: $ref: '#/components/schemas/ProviderSessionId' TopUpStatusDTO: type: string enum: - created - failed - canceled - expired - pending - completed - aml_failed ResponseOutputFormat: type: string enum: - json - csv TopUpRequest: type: object required: - amount properties: amount: type: integer format: int64 currency: $ref: '#/components/schemas/Currency' PaymentMethodDto: type: object required: - provider - methodCode - kind - enabled - displayName - minAmountCents - feePaidByUser - estFeeBps - estFeeFixedCents - sortOrder properties: displayName: type: string enabled: type: boolean estFeeBps: type: integer format: int32 estFeeFixedCents: type: integer format: int64 feePaidByUser: type: boolean kind: $ref: '#/components/schemas/PaymentMethodKind' maxAmountCents: type: - integer - 'null' format: int64 methodCode: type: string minAmountCents: type: integer format: int64 provider: type: string sortOrder: type: integer format: int32 IntentId: type: string format: uuid ChargeGroupByDto: type: string description: 'Resource dimension to aggregate charges by. Defaults to the full `(user, resourceId, resourceType)` tuple when omitted.' enum: - RESOURCE - RESOURCE_TYPE - CLUSTER ErrorBody: type: object required: - error - code properties: code: $ref: '#/components/schemas/ErrorCode' description: The stable, machine-readable error code. error: type: string description: The human-readable error message. TopUpSortFieldDto: type: string description: 'Field to order billing history by. `amount` sorts by the top-up amount; `createdAt` (the default) is newest-first. Distinct from `SortFieldDto` because the billing listings expose `amount` while the CRD listings do not.' enum: - createdAt - amount IpAddressType: type: string enum: - V4 - V6 TopUpId: type: string format: uuid PaginatedResponse_TopUpV2Dto: type: object required: - items - pagination properties: items: type: array items: type: object description: 'v2-native top-up read DTO. Amounts are integer cents (matching the create input and storage — no legacy 10^8 base units); status is the provider-neutral [`TopUpV2Status`]; the redirect URL is provider-neutral (the backend overloads the legacy `copperx_session_url` column for any provider).' required: - id - userId - amountCents - status - createdAt - updatedAt properties: amountCents: type: integer format: int64 createdAt: type: string example: '2024-01-01T00:00:00Z' expiresAt: type: - string - 'null' description: 'When this top-up''s checkout session times out. `null` for legacy `public.top_ups` rows, which have no expiry.' example: '2024-01-01T00:00:00Z' id: $ref: '#/components/schemas/TopUpId' provider: type: - string - 'null' redirectUrl: type: - string - 'null' status: $ref: '#/components/schemas/TopUpV2Status' updatedAt: type: string example: '2024-01-01T00:00:00Z' userId: $ref: '#/components/schemas/UserId' pagination: $ref: '#/components/schemas/PaginationInfo' PaginatedResponse_TopUpDto: type: object required: - items - pagination properties: items: type: array items: type: object required: - id - userId - currency - amount - copperxFee - copperxGasFee - status - createdAt - updatedAt properties: amount: type: integer format: u-int64 minimum: 0 balanceContractTxAddress: type: - string - 'null' copperxFee: type: integer format: u-int64 minimum: 0 copperxGasFee: type: integer format: u-int64 minimum: 0 copperxSessionId: type: - string - 'null' copperxSessionUrl: type: - string - 'null' createdAt: type: string example: '2024-01-01T00:00:00Z' currency: $ref: '#/components/schemas/Currency' expiresAt: type: - string - 'null' description: When this top-up's checkout session times out. Omitted for legacy v1 rows. example: '2024-01-01T00:00:00Z' id: $ref: '#/components/schemas/TopUpId' provider: type: - string - 'null' description: 'Payment provider behind this top-up (`copperx`, `stripe`, ...). The `copperxSession*` fields keep their legacy names for back-compat but carry the session of whatever provider is named here.' status: $ref: '#/components/schemas/TopUpStatusDTO' updatedAt: type: string example: '2024-01-01T00:00:00Z' userId: $ref: '#/components/schemas/UserId' pagination: $ref: '#/components/schemas/PaginationInfo' SortOrderDto: type: string enum: - asc - desc UserId: type: string format: uuid StorageType: type: string enum: - HDD - SSD - NVMe TotalChargesResponse: type: object required: - total properties: total: type: string additionalProperties: type: string TopUpV2Dto: type: object description: 'v2-native top-up read DTO. Amounts are integer cents (matching the create input and storage — no legacy 10^8 base units); status is the provider-neutral [`TopUpV2Status`]; the redirect URL is provider-neutral (the backend overloads the legacy `copperx_session_url` column for any provider).' required: - id - userId - amountCents - status - createdAt - updatedAt properties: amountCents: type: integer format: int64 createdAt: type: string example: '2024-01-01T00:00:00Z' expiresAt: type: - string - 'null' description: 'When this top-up''s checkout session times out. `null` for legacy `public.top_ups` rows, which have no expiry.' example: '2024-01-01T00:00:00Z' id: $ref: '#/components/schemas/TopUpId' provider: type: - string - 'null' redirectUrl: type: - string - 'null' status: $ref: '#/components/schemas/TopUpV2Status' updatedAt: type: string example: '2024-01-01T00:00:00Z' userId: $ref: '#/components/schemas/UserId' PaymentMethodKind: type: string enum: - crypto - fiat PaginationInfo: type: object required: - totalRecords - filteredRecords - totalPages - currentPage - perPage properties: currentPage: type: integer format: u-int64 minimum: 0 filteredRecords: type: integer format: u-int64 minimum: 0 perPage: type: integer format: u-int64 minimum: 0 totalPages: type: integer format: u-int32 minimum: 0 totalRecords: type: integer format: u-int64 minimum: 0 CreateTopUpV2Request: type: object required: - amountCents properties: amountCents: type: integer format: int64 methodCode: type: - string - 'null' description: 'Catalog method the user picked (see `GET /v2/payment-methods`). When absent, the provider''s default crypto method is used.' provider: type: - string - 'null' TotalChargesEntry: type: object required: - userId - totalUsageHours - totalCost properties: clusterId: type: - string - 'null' description: Null when grouping by resource type. example: 9ba32273-49e6-4655-b193-b29f61752c2f ipAddressType: oneOf: - type: 'null' - $ref: '#/components/schemas/IpAddressType' periodStart: type: - string - 'null' description: Start of the time bucket this row aggregates. Present only when `period` was requested. example: '2024-06-10T00:00:00Z' quantity: type: - string - 'null' description: Null when grouping by a dimension coarser than the individual resource. replicatedStorage: type: - boolean - 'null' resourceId: type: - string - 'null' description: Null when grouping by a dimension coarser than the individual resource. example: 9ba32273-49e6-4655-b193-b29f61752c2f resourceName: type: - string - 'null' description: Null when grouping by a dimension coarser than the individual resource. resourceType: oneOf: - type: 'null' - $ref: '#/components/schemas/ResourceTypeDto' description: Null when grouping by cluster. storageType: oneOf: - type: 'null' - $ref: '#/components/schemas/StorageType' totalCost: type: string totalUsageHours: type: string userId: type: string example: 9ba32273-49e6-4655-b193-b29f61752c2f vmConfiguration: oneOf: - type: 'null' - $ref: '#/components/schemas/VmConfigurationSlug' PendingTopUpV2Response: type: object properties: pendingTopUp: oneOf: - type: 'null' - $ref: '#/components/schemas/TopUpV2Dto' TopUpPresentation: oneOf: - type: object description: Hosted checkout / invoice page the user is redirected to. required: - redirectUrl - type properties: redirectUrl: type: string type: type: string enum: - redirect description: 'Typed presentation handed to the client in the v2 top-up response. Stored in the provider session as JSON (`ProviderSession.presentation`); adapters write the tagged form, readers (v2 mappers, v1 shim) accept it with a fallback to the legacy flat `redirect_url` key for in-flight pre-migration sessions. Tagged (`{ "type": "redirect", "redirectUrl": ... }`) so future variants (e.g. a direct crypto-transfer with `payAddress`) are additive, not a breaking field change.' ProviderSessionId: type: string format: uuid ChargePeriodDto: type: string description: 'Time bucket to split aggregated charges into. When omitted, charges are not split by time.' enum: - DAY - MONTH PendingTopUpResponse: type: object properties: pendingTopUp: oneOf: - type: 'null' - $ref: '#/components/schemas/TopUpDto' ResourceId: type: string format: uuid TopUpV2Status: type: string description: 'Lifecycle status exposed by the v2 top-up read DTO. It is `IntentStatus` plus `Failed`: terminal legacy (copperx) failures have no `IntentStatus` image, and collapsing them into `Canceled`/`Expired` would misreport a failure as a clean end state.' enum: - open - needs_review - resolved - canceled - expired - failed securitySchemes: access_jwt_token: type: http scheme: bearer bearerFormat: JWT description: JWT token issued by Fluence api_key: type: apiKey in: header name: X-API-KEY