Originally published on the Hubfly blog.
By Octave · 18 Oct 2026
TL;DR
- What you get: a pattern for giving each customer their own wallet, projects and machine key on Hubfly, using sub-accounts. Three control-plane calls per customer, then the normal API.
- Who it's for: SaaS teams, agencies and AI-agent builders who run infrastructure for their customers.
- Time: about 10 minutes to read. Creating, suspending and revoking a tenant in the dashboard took us about 2 minutes.
The problem: one customer's bill is everyone's bill
You run a SaaS. Each customer gets a database, a worker or a preview app. On most clouds, all of that lands in one account and one bill.
Then customer 1042's import job loops all weekend. You find out from your card statement. You also can't tell which customer owes what, and one leaked key reaches every tenant.
The textbook answer is the "silo" model: a separate account per customer. On AWS this usually means AWS Organizations and an account-vending process. Most guides stop at the diagram. This post shows the actual calls on Hubfly, and the limits.
What Hubfly sub-accounts are
A sub-account is a tenant, not a team member. It has no password, no login and no invitations. Your software talks to it with a machine key (docs).
Each tenant gets:
-
Its own wallet, with paid and promotional balances, in integer micro-USD (
1000000= $1.00). - Its own projects, which are ordinary Hubfly projects: containers, Compose, volumes, domains, firewall, logs.
-
Its own keys. An
hfsk_key is locked to one tenant. A parenthfpk_key can manage all your tenants and target one with theX-HubFly-Subaccountheader. -
An
externalRef, unique inside your parent, so you can mapcustomer_1042in your database tosub_…in Hubfly.
The docs list the use cases plainly: SaaS customer infrastructure, AI agents, managed hosting, preview environments and internal developer portals.
Your app (parent hfpk_ key)
├── sub_A wallet $25 ── project ── containers, volumes, domains
├── sub_B wallet $10 ── project ── ...
└── sub_C suspended (keys disabled)
Walkthrough: what we did in the dashboard
We tested this in our own marketing account on 17 Oct 2026. Open Profile → Sub-accounts.
-
Create. We clicked New sub-account, typed the name
blog-lab-tenant-demoand the external referencebloglab_tenant_001, and clicked Create sub-account. It showed as Active with asub_…ID 23 seconds after we opened the dialog. - Inspect. Manage opens a panel with the wallet balance ($0.00), active keys, status, a Fund wallet box ("Move paid balance from the parent account"), Machine credentials and Suspend account.
-
Key (the surprise). We clicked New key expecting a form. There was none. The dashboard immediately created a key called Master key and showed the
hfsk_secret once, with "HubFly stores only a hash of this key and cannot recover it later." We didn't need it, so we closed the dialog and clicked Revoke. Under two minutes after we created the tenant, the banner said "API key revoked." and active keys were back to 0. Note that Revoke also has no confirmation step.
- Suspend. Suspend account worked in one click, again with no confirmation. Status changed to Suspended, New key greyed out, and the button became Resume account.
Everything above cost $0.00. We didn't fund the wallet or deploy into the tenant, so this post doesn't show a tenant workload running. We also didn't find a way to close a tenant in the dashboard, so ours is parked as suspended.
The production flow: 4 calls to onboard, 1 to close
The dashboard is fine for a look around. For real customers, provision from your backend with a parent platform key. These come from the Sub-account API guide; all paths are under https://api.hubfly.space/api/v1. From the docs: we did not run these API calls in our test; we used the dashboard only.
1. Create the tenant. Use an idempotency key, so a retry returns the same tenant instead of a duplicate.
curl -X POST https://api.hubfly.space/api/v1/subaccounts/create \
-H "Authorization: Bearer $HUBFLY_PLATFORM_TOKEN" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: tenant-customer-1042-v1" \
-d '{"name":"Customer 1042","externalRef":"customer_1042"}'
2. Fund its wallet. This moves paid balance from your parent wallet. Promotional credit can't be transferred.
curl -X POST https://api.hubfly.space/api/v1/subaccounts/$SUBACCOUNT_ID/wallet/transfers \
-H "Authorization: Bearer $HUBFLY_PLATFORM_TOKEN" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: funding-customer-1042-2026-10" \
-d '{"amountMicroUsd":"25000000","idempotencyKey":"funding-customer-1042-2026-10"}'
3. Mint a tenant key and write it straight to your secret manager. It's shown once.
curl -X POST https://api.hubfly.space/api/v1/subaccounts/$SUBACCOUNT_ID/api-keys/create \
-H "Authorization: Bearer $HUBFLY_PLATFORM_TOKEN" \
-H "Content-Type: application/json" \
-d '{"name":"production workload"}'
4. Use the normal endpoints. There are no special tenant URLs. With the tenant key, GET /projects lists only that tenant's projects. With your platform key, add -H "X-HubFly-Subaccount: $SUBACCOUNT_ID". The TypeScript and Go SDKs and the CLI (--subaccount sub_… or HUBFLY_SUBACCOUNT) support the same pattern.
5. Close when the customer leaves (from the docs). Stop and remove the tenant's projects, settle any negative balance, then call POST /subaccounts/$SUBACCOUNT_ID/close. Eligible paid balance returns to your parent wallet; promotional balance expires; keys are revoked.
Isolation rules from the docs: an hfsk_ key sent with a different tenant header gets 403 SUBACCOUNT_SCOPE, and a platform key can't target a tenant outside its parent.
Limits and pricing (honest version)
Limits from the docs:
- 25 active sub-accounts per parent and 10 active keys per parent or tenant, by default.
- One level deep: a tenant can't create tenants. You can't move a tenant to another parent.
- No GPU, no GitHub OAuth repo discovery, no team members or invitations, no payment methods inside a tenant.
- No automatic top-up for tenant wallets in the first release.
- Suspending doesn't stop running resources. The docs say to stop billable resources before you suspend.
- Closing needs no active projects and no negative balance. Force close isn't available yet.
Pricing: sub-accounts have no listed fee. A tenant pays normal Hubfly rates from its own wallet: $0.004 per vCPU-hour, $0.002 per GB-hour of RAM, $0.00001536 per GB-hour of Standard storage.
| Per tenant, always on (730 h) | Math | Monthly |
|---|---|---|
| 1 vCPU, 0.5 GB RAM, 5 GB disk | 2.92 + 0.73 + 0.06 | $3.71 |
| 0.5 vCPU, 0.5 GB RAM, 5 GB disk | 1.46 + 0.73 + 0.06 | $2.25 |
| 25 tenants on the first size | 25 × $3.706 | $92.65 |
Whether each tenant gets its own 360 free hours isn't documented, so don't price your plans on it. Per the pricing page, sleeping containers pause compute billing; volumes are still billed.
What can go wrong
- You mint keys by accident. As we found, one click on New key creates a key named Master key. Create keys from your backend, name them per workload, and revoke old ones after rotation.
- You suspend a tenant and it keeps billing. Suspension disables credentials and changes, not running containers. Stop the tenant's workloads first, then suspend.
-
A customer passes someone else's tenant ID. Never accept a
subaccountIdfrom the client. Resolve it from your own customer record, and alert on403 SUBACCOUNT_SCOPE. -
409 API_KEY_LIMIT_REACHED. A tenant holds 10 active keys by default. Revoke unused keys before creating another.
Bonus: an ambiguous funding failure. Don't retry with a new idempotency key until you've read the wallet; you could fund twice.
Who this is NOT for
- Your own team. For colleagues, use an organization with roles. Sub-accounts have no logins.
- GPU workloads per customer. Not supported in tenants today.
- More than 25 customers, today, with no plan. That's the default cap. Talk to us first if you need more.
- Customers who must hold their own bill. Tenants have no payment methods; you fund them from your paid balance.
Takeaway
If you sell software that runs on infrastructure, sub-accounts give you the silo model without account vending: one wallet, one project namespace and one revocable key per customer. Provision from your backend with idempotency keys, keep keys out of the dashboard, and stop workloads before you suspend.
Read next: Let your coding agent deploy to Hubfly safely (give each agent its own tenant), Gate your app on a Postgres migration job and 502 Bad Gateway in Docker? Bind to 0.0.0.0.
Building a platform on Hubfly? Talk to sales about tenant limits, or start free with 360 hours included and create your first sub-account in Profile → Sub-accounts.




Top comments (0)